summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/processor
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /toolkit/crashreporter/google-breakpad/src/processor
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/address_map-inl.h93
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/address_map.h85
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/address_map_unittest.cc196
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_code_module.h116
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.cc155
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.h98
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc612
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_types.h177
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc682
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/call_stack.cc54
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h119
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc186
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h275
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc546
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h197
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/contained_range_map.h150
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/contained_range_map_unittest.cc263
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc240
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.h127
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86_unittest.cc233
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/dump_context.cc659
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/dump_object.cc39
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability.cc119
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.cc625
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.h129
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability_unittest.cc306
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.cc283
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.h55
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver.cc275
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_types.h185
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_unittest.cc491
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/linked_ptr.h193
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/logging.cc111
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/logging.h186
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/map_serializers-inl.h266
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/map_serializers.h168
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/map_serializers_unittest.cc386
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/microdump.cc385
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/microdump_processor.cc100
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/microdump_processor_unittest.cc273
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk.cc151
-rwxr-xr-xtoolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_machine_readable_test43
-rwxr-xr-xtoolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test43
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test_vars1
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump.cc4989
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc213
-rwxr-xr-xtoolkit/crashreporter/google-breakpad/src/processor/minidump_dump_test36
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc1577
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc645
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc162
-rwxr-xr-xtoolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_machine_readable_test37
-rwxr-xr-xtoolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_test37
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc1521
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/module_comparer.cc302
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/module_comparer.h98
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/module_factory.h72
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/module_serializer.cc207
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/module_serializer.h127
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/moz.build66
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.cc56
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.h53
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc87
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h363
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h179
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc403
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux.cc106
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux_unittest.cc251
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/process_state.cc69
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/processor.gyp184
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/processor_tools.gypi57
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/proto/README20
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/proto/process_state.proto210
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h272
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/range_map.h161
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/range_map_shrink_down_unittest.cc355
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc559
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/simple_serializer-inl.h260
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/simple_serializer.h63
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc204
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h140
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base.cc341
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base_types.h158
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stack_frame_cpu.cc79
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stack_frame_symbolizer.cc138
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.cc950
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.h49
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc296
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.cc92
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.h72
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list_unittest.cc197
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc340
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h116
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc932
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc296
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h107
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.cc278
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.h104
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64_unittest.cc880
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc974
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.cc448
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.h85
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips_unittest.cc707
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc146
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h79
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.cc137
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.h77
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest.cc433
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest_sol.s111
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc139
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h78
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h224
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc672
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h117
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc2128
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_address_map-inl.h71
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_address_map.h78
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_address_map_unittest.cc236
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map-inl.h92
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map.h96
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map_unittest.cc320
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_map-inl.h176
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_map.h144
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator-inl.h147
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator.h112
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_map_unittest.cc386
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_range_map-inl.h130
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_range_map.h106
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/static_range_map_unittest.cc421
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.cc6418
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.h50
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc391
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h372
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc336
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h418
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/tokenize.cc79
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/tokenize.h63
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h209
137 files changed, 47048 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/address_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/address_map-inl.h
new file mode 100644
index 000000000..251c44781
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/address_map-inl.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// address_map-inl.h: Address map implementation.
+//
+// See address_map.h for documentation.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_ADDRESS_MAP_INL_H__
+#define PROCESSOR_ADDRESS_MAP_INL_H__
+
+#include "processor/address_map.h"
+
+#include <assert.h>
+
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+bool AddressMap<AddressType, EntryType>::Store(const AddressType &address,
+ const EntryType &entry) {
+ // Ensure that the specified address doesn't conflict with something already
+ // in the map.
+ if (map_.find(address) != map_.end()) {
+ BPLOG(INFO) << "Store failed, address " << HexString(address) <<
+ " is already present";
+ return false;
+ }
+
+ map_.insert(MapValue(address, entry));
+ return true;
+}
+
+template<typename AddressType, typename EntryType>
+bool AddressMap<AddressType, EntryType>::Retrieve(
+ const AddressType &address,
+ EntryType *entry, AddressType *entry_address) const {
+ BPLOG_IF(ERROR, !entry) << "AddressMap::Retrieve requires |entry|";
+ assert(entry);
+
+ // upper_bound gives the first element whose key is greater than address,
+ // but we want the first element whose key is less than or equal to address.
+ // Decrement the iterator to get there, but not if the upper_bound already
+ // points to the beginning of the map - in that case, address is lower than
+ // the lowest stored key, so return false.
+ MapConstIterator iterator = map_.upper_bound(address);
+ if (iterator == map_.begin())
+ return false;
+ --iterator;
+
+ *entry = iterator->second;
+ if (entry_address)
+ *entry_address = iterator->first;
+
+ return true;
+}
+
+template<typename AddressType, typename EntryType>
+void AddressMap<AddressType, EntryType>::Clear() {
+ map_.clear();
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_ADDRESS_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/address_map.h b/toolkit/crashreporter/google-breakpad/src/processor/address_map.h
new file mode 100644
index 000000000..2972cbb9f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/address_map.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// address_map.h: Address maps.
+//
+// An address map contains a set of objects keyed by address. Objects are
+// retrieved from the map by returning the object with the highest key less
+// than or equal to the lookup key.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_ADDRESS_MAP_H__
+#define PROCESSOR_ADDRESS_MAP_H__
+
+#include <map>
+
+namespace google_breakpad {
+
+// Forward declarations (for later friend declarations).
+template<class, class> class AddressMapSerializer;
+
+template<typename AddressType, typename EntryType>
+class AddressMap {
+ public:
+ AddressMap() : map_() {}
+
+ // Inserts an entry into the map. Returns false without storing the entry
+ // if an entry is already stored in the map at the same address as specified
+ // by the address argument.
+ bool Store(const AddressType &address, const EntryType &entry);
+
+ // Locates the entry stored at the highest address less than or equal to
+ // the address argument. If there is no such range, returns false. The
+ // entry is returned in entry, which is a required argument. If
+ // entry_address is not NULL, it will be set to the address that the entry
+ // was stored at.
+ bool Retrieve(const AddressType &address,
+ EntryType *entry, AddressType *entry_address) const;
+
+ // Empties the address map, restoring it to the same state as when it was
+ // initially created.
+ void Clear();
+
+ private:
+ friend class AddressMapSerializer<AddressType, EntryType>;
+ friend class ModuleComparer;
+
+ // Convenience types.
+ typedef std::map<AddressType, EntryType> AddressToEntryMap;
+ typedef typename AddressToEntryMap::const_iterator MapConstIterator;
+ typedef typename AddressToEntryMap::value_type MapValue;
+
+ // Maps the address of each entry to an EntryType.
+ AddressToEntryMap map_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_ADDRESS_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/address_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/address_map_unittest.cc
new file mode 100644
index 000000000..9b4095b16
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/address_map_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// address_map_unittest.cc: Unit tests for AddressMap.
+//
+// Author: Mark Mentovai
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "processor/address_map-inl.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+
+#define ASSERT_TRUE(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
+ return false; \
+ }
+
+#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
+
+#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
+
+namespace {
+
+using google_breakpad::AddressMap;
+using google_breakpad::linked_ptr;
+
+// A CountedObject holds an int. A global (not thread safe!) count of
+// allocated CountedObjects is maintained to help test memory management.
+class CountedObject {
+ public:
+ explicit CountedObject(int id) : id_(id) { ++count_; }
+ ~CountedObject() { --count_; }
+
+ static int count() { return count_; }
+ int id() const { return id_; }
+
+ private:
+ static int count_;
+ int id_;
+};
+
+int CountedObject::count_;
+
+typedef int AddressType;
+typedef AddressMap< AddressType, linked_ptr<CountedObject> > TestMap;
+
+static bool DoAddressMapTest() {
+ ASSERT_EQ(CountedObject::count(), 0);
+
+ TestMap test_map;
+ linked_ptr<CountedObject> entry;
+ AddressType address;
+
+ // Check that a new map is truly empty.
+ ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
+ ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
+ ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
+
+ // Check that Clear clears the map without leaking.
+ ASSERT_EQ(CountedObject::count(), 0);
+ ASSERT_TRUE(test_map.Store(1,
+ linked_ptr<CountedObject>(new CountedObject(0))));
+ ASSERT_TRUE(test_map.Retrieve(1, &entry, &address));
+ ASSERT_EQ(CountedObject::count(), 1);
+ test_map.Clear();
+ ASSERT_EQ(CountedObject::count(), 1); // still holding entry in this scope
+
+ // Check that a cleared map is truly empty.
+ ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
+ ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
+ ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
+
+ // Check a single-element map.
+ ASSERT_TRUE(test_map.Store(10,
+ linked_ptr<CountedObject>(new CountedObject(1))));
+ ASSERT_FALSE(test_map.Retrieve(9, &entry, &address));
+ ASSERT_TRUE(test_map.Retrieve(10, &entry, &address));
+ ASSERT_EQ(CountedObject::count(), 1);
+ ASSERT_EQ(entry->id(), 1);
+ ASSERT_EQ(address, 10);
+ ASSERT_TRUE(test_map.Retrieve(11, &entry, &address));
+ ASSERT_TRUE(test_map.Retrieve(11, &entry, NULL)); // NULL ok here
+
+ // Add some more elements.
+ ASSERT_TRUE(test_map.Store(5,
+ linked_ptr<CountedObject>(new CountedObject(2))));
+ ASSERT_EQ(CountedObject::count(), 2);
+ ASSERT_TRUE(test_map.Store(20,
+ linked_ptr<CountedObject>(new CountedObject(3))));
+ ASSERT_TRUE(test_map.Store(15,
+ linked_ptr<CountedObject>(new CountedObject(4))));
+ ASSERT_FALSE(test_map.Store(10,
+ linked_ptr<CountedObject>(new CountedObject(5)))); // already in map
+ ASSERT_TRUE(test_map.Store(16,
+ linked_ptr<CountedObject>(new CountedObject(6))));
+ ASSERT_TRUE(test_map.Store(14,
+ linked_ptr<CountedObject>(new CountedObject(7))));
+
+ // Nothing was stored with a key under 5. Don't use ASSERT inside loops
+ // because it won't show exactly which key/entry/address failed.
+ for (AddressType key = 0; key < 5; ++key) {
+ if (test_map.Retrieve(key, &entry, &address)) {
+ fprintf(stderr,
+ "FAIL: retrieve %d expected false observed true @ %s:%d\n",
+ key, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ // Check everything that was stored.
+ const int id_verify[] = { 0, 0, 0, 0, 0, // unused
+ 2, 2, 2, 2, 2, // 5 - 9
+ 1, 1, 1, 1, 7, // 10 - 14
+ 4, 6, 6, 6, 6, // 15 - 19
+ 3, 3, 3, 3, 3, // 20 - 24
+ 3, 3, 3, 3, 3 }; // 25 - 29
+ const AddressType address_verify[] = { 0, 0, 0, 0, 0, // unused
+ 5, 5, 5, 5, 5, // 5 - 9
+ 10, 10, 10, 10, 14, // 10 - 14
+ 15, 16, 16, 16, 16, // 15 - 19
+ 20, 20, 20, 20, 20, // 20 - 24
+ 20, 20, 20, 20, 20 }; // 25 - 29
+
+ for (AddressType key = 5; key < 30; ++key) {
+ if (!test_map.Retrieve(key, &entry, &address)) {
+ fprintf(stderr,
+ "FAIL: retrieve %d expected true observed false @ %s:%d\n",
+ key, __FILE__, __LINE__);
+ return false;
+ }
+ if (entry->id() != id_verify[key]) {
+ fprintf(stderr,
+ "FAIL: retrieve %d expected entry %d observed %d @ %s:%d\n",
+ key, id_verify[key], entry->id(), __FILE__, __LINE__);
+ return false;
+ }
+ if (address != address_verify[key]) {
+ fprintf(stderr,
+ "FAIL: retrieve %d expected address %d observed %d @ %s:%d\n",
+ key, address_verify[key], address, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ // The stored objects should still be in the map.
+ ASSERT_EQ(CountedObject::count(), 6);
+
+ return true;
+}
+
+static bool RunTests() {
+ if (!DoAddressMapTest())
+ return false;
+
+ // Leak check.
+ ASSERT_EQ(CountedObject::count(), 0);
+
+ return true;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ return RunTests() ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_code_module.h b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_module.h
new file mode 100644
index 000000000..0f7b3e431
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_module.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_module.h: Carries information about code modules that are loaded
+// into a process.
+//
+// This is a basic concrete implementation of CodeModule. It cannot be
+// instantiated directly, only based on other objects that implement
+// the CodeModule interface. It exists to provide a CodeModule implementation
+// a place to store information when the life of the original object (such as
+// a MinidumpModule) cannot be guaranteed.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_BASIC_CODE_MODULE_H__
+#define PROCESSOR_BASIC_CODE_MODULE_H__
+
+#include <string>
+
+#include "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()) {}
+
+ 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)
+ : 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)
+ {}
+ 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); }
+
+ 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_;
+
+ // Disallow copy constructor and assignment operator.
+ BasicCodeModule(const BasicCodeModule &that);
+ void operator=(const BasicCodeModule &that);
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_BASIC_CODE_MODULE_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.cc b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.cc
new file mode 100644
index 000000000..48d971677
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_modules.cc: Contains all of the CodeModule objects that
+// were loaded into a single process.
+//
+// See basic_code_modules.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "processor/basic_code_modules.h"
+
+#include <assert.h>
+
+#include <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)
+ : main_address_(0), map_() {
+ BPLOG_IF(ERROR, !that) << "BasicCodeModules::BasicCodeModules requires "
+ "|that|";
+ assert(that);
+
+ map_.SetEnableShrinkDown(that->IsModuleShrinkEnabled());
+
+ 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);
+}
+
+vector<linked_ptr<const CodeModule> >
+BasicCodeModules::GetShrunkRangeModules() const {
+ return shrunk_range_modules_;
+}
+
+bool BasicCodeModules::IsModuleShrinkEnabled() const {
+ return map_.IsShrinkDownEnabled();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.h b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.h
new file mode 100644
index 000000000..50f8a03d8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_code_modules.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_modules.h: Contains all of the CodeModule objects that
+// were loaded into a single process.
+//
+// This is a basic concrete implementation of CodeModules. It cannot be
+// instantiated directly, only based on other objects that implement
+// the CodeModules interface. It exists to provide a CodeModules
+// implementation a place to store information when the life of the original
+// object (such as a MinidumpModuleList) cannot be guaranteed.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_BASIC_CODE_MODULES_H__
+#define PROCESSOR_BASIC_CODE_MODULES_H__
+
+#include <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.
+ explicit BasicCodeModules(const CodeModules *that);
+
+ virtual ~BasicCodeModules();
+
+ // See code_modules.h for descriptions of these methods.
+ virtual unsigned int module_count() const;
+ virtual const CodeModule* GetModuleForAddress(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;
+ virtual bool IsModuleShrinkEnabled() 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc
new file mode 100644
index 000000000..aa66e1599
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc
@@ -0,0 +1,612 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// basic_source_line_resolver.cc: BasicSourceLineResolver implementation.
+//
+// See basic_source_line_resolver.h and basic_source_line_resolver_types.h
+// for documentation.
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <limits>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "processor/basic_source_line_resolver_types.h"
+#include "processor/module_factory.h"
+
+#include "processor/tokenize.h"
+
+using std::map;
+using std::vector;
+using std::make_pair;
+
+namespace google_breakpad {
+
+#ifdef _WIN32
+#ifdef _MSC_VER
+#define strtok_r strtok_s
+#endif
+#define strtoull _strtoui64
+#endif
+
+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;
+ 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 (!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::LookupAddress(StackFrame *frame) const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+
+ // First, look for a FUNC record that covers address. Use
+ // RetrieveNearestRange instead of RetrieveRange so that, if there
+ // is no such function, we can use the next function to bound the
+ // extent of the PUBLIC symbol we find, below. This does mean we
+ // need to check that address indeed falls within the function we
+ // find; do the range comparison in an overflow-friendly way.
+ linked_ptr<Function> func;
+ linked_ptr<PublicSymbol> public_symbol;
+ MemAddr function_base;
+ MemAddr function_size;
+ MemAddr public_address;
+ if (functions_.RetrieveNearestRange(address, &func, &function_base,
+ 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;
+
+ 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;
+ }
+ } else if (public_symbols_.Retrieve(address,
+ &public_symbol, &public_address) &&
+ (!func.get() || public_address > function_base)) {
+ frame->function_name = public_symbol->name;
+ frame->function_base = frame->module->base_address() + public_address;
+ }
+}
+
+WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
+ const StackFrame *frame) const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+ scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
+
+ // We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and
+ // WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order.
+ // WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that
+ // includes its own program string.
+ // WindowsFrameInfo::STACK_INFO_FPO is the older type
+ // corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
+ linked_ptr<WindowsFrameInfo> frame_info;
+ if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA]
+ .RetrieveRange(address, &frame_info))
+ || (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO]
+ .RetrieveRange(address, &frame_info))) {
+ result->CopyFrom(*frame_info.get());
+ return result.release();
+ }
+
+ // Even without a relevant STACK line, many functions contain
+ // information about how much space their parameters consume on the
+ // stack. Use RetrieveNearestRange instead of RetrieveRange, so that
+ // we can use the function to bound the extent of the PUBLIC symbol,
+ // below. However, this does mean we need to check that ADDRESS
+ // falls within the retrieved function's range; do the range
+ // comparison in an overflow-friendly way.
+ linked_ptr<Function> function;
+ MemAddr function_base, function_size;
+ if (functions_.RetrieveNearestRange(address, &function, &function_base,
+ 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;
+}
+
+BasicSourceLineResolver::Function*
+BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
+ uint64_t address;
+ uint64_t size;
+ long stack_param_size;
+ char *name;
+ if (SymbolParseHelper::ParseFunction(function_line, &address, &size,
+ &stack_param_size, &name)) {
+ return new Function(name, address, size, stack_param_size);
+ }
+ 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) {
+ uint64_t address;
+ long stack_param_size;
+ char *name;
+
+ if (SymbolParseHelper::ParsePublicSymbol(public_line, &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));
+ 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;
+}
+
+// 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::ParseFunction(char *function_line, uint64_t *address,
+ uint64_t *size, long *stack_param_size,
+ char **name) {
+ // FUNC <address> <size> <stack_param_size> <name>
+ assert(strncmp(function_line, "FUNC ", 5) == 0);
+ function_line += 5; // skip prefix
+
+ vector<char*> tokens;
+ if (!Tokenize(function_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;
+ }
+ *stack_param_size = strtol(tokens[2], &after_number, 16);
+ if (!IsValidAfterNumber(after_number) ||
+ *stack_param_size == std::numeric_limits<long>::max() ||
+ *stack_param_size < 0) {
+ return false;
+ }
+ *name = tokens[3];
+
+ 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,
+ uint64_t *address,
+ long *stack_param_size,
+ char **name) {
+ // PUBLIC <address> <stack_param_size> <name>
+ assert(strncmp(public_line, "PUBLIC ", 7) == 0);
+ public_line += 7; // skip prefix
+
+ vector<char*> tokens;
+ if (!Tokenize(public_line, kWhitespace, 3, &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;
+ }
+ *stack_param_size = strtol(tokens[1], &after_number, 16);
+ if (!IsValidAfterNumber(after_number) ||
+ *stack_param_size == std::numeric_limits<long>::max() ||
+ *stack_param_size < 0) {
+ return false;
+ }
+ *name = tokens[2];
+
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_types.h b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_types.h
new file mode 100644
index 000000000..a022bc0db
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_types.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// basic_source_line_types.h: definition of nested classes/structs in
+// BasicSourceLineResolver. It moves the definitions out of
+// basic_source_line_resolver.cc, so that other classes could have access
+// to these private nested types without including basic_source_line_resolver.cc
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__
+#define PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__
+
+#include <map>
+#include <string>
+
+#include "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) : Base(function_name,
+ function_address,
+ code_size,
+ set_parameter_size),
+ lines() { }
+ RangeMap< MemAddr, linked_ptr<Line> > lines;
+ private:
+ typedef SourceLineResolverBase::Function Base;
+};
+
+
+class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module {
+ public:
+ explicit Module(const string &name) : name_(name), 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) 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 a function declaration, returning a new Function object.
+ Function* ParseFunction(char *function_line);
+
+ // Parses a line declaration, returning a new Line object.
+ Line* ParseLine(char *line_line);
+
+ // Parses a PUBLIC symbol declaration, storing it in public_symbols_.
+ // Returns false if an error occurs.
+ bool ParsePublicSymbol(char *public_line);
+
+ // Parses a STACK WIN or STACK CFI frame info declaration, storing
+ // it in the appropriate table.
+ bool ParseStackInfo(char *stack_info_line);
+
+ // Parses a STACK CFI record, storing it in cfi_frame_info_.
+ bool ParseCFIFrameInfo(char *stack_info_line);
+
+ string name_;
+ FileMap files_;
+ RangeMap< MemAddr, linked_ptr<Function> > functions_;
+ AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
+ 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc
new file mode 100644
index 000000000..a75044c74
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc
@@ -0,0 +1,682 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <string>
+
+#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/code_module.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+#include "processor/windows_frame_info.h"
+#include "processor/cfi_frame_info.h"
+
+namespace {
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CFIFrameInfo;
+using google_breakpad::CodeModule;
+using google_breakpad::MemoryRegion;
+using google_breakpad::StackFrame;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::linked_ptr;
+using google_breakpad::scoped_ptr;
+using google_breakpad::SymbolParseHelper;
+
+class TestCodeModule : public CodeModule {
+ public:
+ 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 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 TestBasicSourceLineResolver : public ::testing::Test {
+public:
+ void SetUp() {
+ testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
+ "/src/processor/testdata";
+ }
+
+ BasicSourceLineResolver resolver;
+ string testdata_dir;
+};
+
+TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
+{
+ TestCodeModule module1("module1");
+ ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
+ ASSERT_TRUE(resolver.HasModule(&module1));
+ TestCodeModule module2("module2");
+ ASSERT_TRUE(resolver.LoadModule(&module2, testdata_dir + "/module2.out"));
+ ASSERT_TRUE(resolver.HasModule(&module2));
+
+
+ StackFrame frame;
+ scoped_ptr<WindowsFrameInfo> windows_frame_info;
+ scoped_ptr<CFIFrameInfo> cfi_frame_info;
+ frame.instruction = 0x1000;
+ frame.module = NULL;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_FALSE(frame.module);
+ ASSERT_TRUE(frame.function_name.empty());
+ ASSERT_EQ(frame.function_base, 0U);
+ ASSERT_TRUE(frame.source_file_name.empty());
+ ASSERT_EQ(frame.source_line, 0);
+ ASSERT_EQ(frame.source_line_base, 0U);
+
+ frame.module = &module1;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function1_1");
+ ASSERT_TRUE(frame.module);
+ ASSERT_EQ(frame.module->code_file(), "module1");
+ ASSERT_EQ(frame.function_base, 0x1000U);
+ ASSERT_EQ(frame.source_file_name, "file1_1.cc");
+ ASSERT_EQ(frame.source_line, 44);
+ ASSERT_EQ(frame.source_line_base, 0x1000U);
+ windows_frame_info.reset(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_EQ(windows_frame_info->program_string,
+ "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
+
+ ClearSourceLineInfo(&frame);
+ frame.instruction = 0x800;
+ frame.module = &module1;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_TRUE(VerifyEmpty(frame));
+ windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_FALSE(windows_frame_info.get());
+
+ frame.instruction = 0x1280;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function1_3");
+ ASSERT_TRUE(frame.source_file_name.empty());
+ ASSERT_EQ(frame.source_line, 0);
+ windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_TRUE(windows_frame_info.get());
+ ASSERT_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;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function1_4");
+ ASSERT_TRUE(frame.source_file_name.empty());
+ ASSERT_EQ(frame.source_line, 0);
+ windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
+ ASSERT_TRUE(windows_frame_info.get());
+ ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
+ ASSERT_FALSE(windows_frame_info->program_string.empty());
+
+ frame.instruction = 0x2000;
+ windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_FALSE(windows_frame_info.get());
+
+ // module1 has STACK CFI records covering 3d40..3def;
+ // module2 has STACK CFI records covering 3df0..3e9f;
+ // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
+ frame.instruction = 0x3d3f;
+ frame.module = &module1;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_FALSE(cfi_frame_info.get());
+
+ frame.instruction = 0x3e9f;
+ frame.module = &module1;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_FALSE(cfi_frame_info.get());
+
+ CFIFrameInfo::RegisterValueMap<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(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(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(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(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(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(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;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, string("PublicSymbol"));
+
+ frame.instruction = 0x4000;
+ frame.module = &module1;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, string("LargeFunction"));
+
+ frame.instruction = 0x2181;
+ frame.module = &module2;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function2_2");
+ ASSERT_EQ(frame.function_base, 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);
+ windows_frame_info.reset(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;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Public2_1");
+
+ ClearSourceLineInfo(&frame);
+ frame.instruction = 0x219f;
+ frame.module = &module2;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_TRUE(frame.function_name.empty());
+
+ frame.instruction = 0x21a0;
+ frame.module = &module2;
+ resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Public2_2");
+}
+
+TEST_F(TestBasicSourceLineResolver, TestInvalidLoads)
+{
+ TestCodeModule module3("module3");
+ ASSERT_TRUE(resolver.LoadModule(&module3,
+ testdata_dir + "/module3_bad.out"));
+ ASSERT_TRUE(resolver.HasModule(&module3));
+ ASSERT_TRUE(resolver.IsModuleCorrupt(&module3));
+ TestCodeModule module4("module4");
+ ASSERT_TRUE(resolver.LoadModule(&module4,
+ testdata_dir + "/module4_bad.out"));
+ ASSERT_TRUE(resolver.HasModule(&module4));
+ ASSERT_TRUE(resolver.IsModuleCorrupt(&module4));
+ TestCodeModule module5("module5");
+ ASSERT_FALSE(resolver.LoadModule(&module5,
+ testdata_dir + "/invalid-filename"));
+ ASSERT_FALSE(resolver.HasModule(&module5));
+ TestCodeModule invalidmodule("invalid-module");
+ ASSERT_FALSE(resolver.HasModule(&invalidmodule));
+}
+
+TEST_F(TestBasicSourceLineResolver, TestUnload)
+{
+ TestCodeModule module1("module1");
+ ASSERT_FALSE(resolver.HasModule(&module1));
+ ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
+ ASSERT_TRUE(resolver.HasModule(&module1));
+ resolver.UnloadModule(&module1);
+ ASSERT_FALSE(resolver.HasModule(&module1));
+ ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
+ ASSERT_TRUE(resolver.HasModule(&module1));
+}
+
+// Test parsing of valid FILE lines. The format is:
+// FILE <id> <filename>
+TEST(SymbolParseHelper, ParseFileValid) {
+ long index;
+ char *filename;
+
+ char kTestLine[] = "FILE 1 file name";
+ ASSERT_TRUE(SymbolParseHelper::ParseFile(kTestLine, &index, &filename));
+ EXPECT_EQ(1, index);
+ EXPECT_EQ("file name", string(filename));
+
+ // 0 is a valid index.
+ char kTestLine1[] = "FILE 0 file name";
+ ASSERT_TRUE(SymbolParseHelper::ParseFile(kTestLine1, &index, &filename));
+ EXPECT_EQ(0, index);
+ EXPECT_EQ("file name", string(filename));
+}
+
+// Test parsing of invalid FILE lines. The format is:
+// FILE <id> <filename>
+TEST(SymbolParseHelper, ParseFileInvalid) {
+ long index;
+ char *filename;
+
+ // Test missing file name.
+ char kTestLine[] = "FILE 1 ";
+ ASSERT_FALSE(SymbolParseHelper::ParseFile(kTestLine, &index, &filename));
+
+ // Test bad index.
+ char kTestLine1[] = "FILE x1 file name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFile(kTestLine1, &index, &filename));
+
+ // Test large index.
+ char kTestLine2[] = "FILE 123123123123123123123123 file name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFile(kTestLine2, &index, &filename));
+
+ // Test negative index.
+ char kTestLine3[] = "FILE -2 file name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFile(kTestLine3, &index, &filename));
+}
+
+// Test parsing of valid FUNC lines. The format is:
+// FUNC <address> <size> <stack_param_size> <name>
+TEST(SymbolParseHelper, ParseFunctionValid) {
+ uint64_t address;
+ uint64_t size;
+ long stack_param_size;
+ char *name;
+
+ char kTestLine[] = "FUNC 1 2 3 function name";
+ ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine, &address, &size,
+ &stack_param_size, &name));
+ EXPECT_EQ(1ULL, address);
+ EXPECT_EQ(2ULL, size);
+ EXPECT_EQ(3, stack_param_size);
+ EXPECT_EQ("function name", string(name));
+
+ // Test hex address, size, and param size.
+ char kTestLine1[] = "FUNC a1 a2 a3 function name";
+ ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine1, &address, &size,
+ &stack_param_size, &name));
+ EXPECT_EQ(0xa1ULL, address);
+ EXPECT_EQ(0xa2ULL, size);
+ EXPECT_EQ(0xa3, stack_param_size);
+ EXPECT_EQ("function name", string(name));
+
+ char kTestLine2[] = "FUNC 0 0 0 function name";
+ ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine2, &address, &size,
+ &stack_param_size, &name));
+ EXPECT_EQ(0ULL, address);
+ EXPECT_EQ(0ULL, size);
+ EXPECT_EQ(0, stack_param_size);
+ EXPECT_EQ("function name", string(name));
+}
+
+// Test parsing of invalid FUNC lines. The format is:
+// FUNC <address> <size> <stack_param_size> <name>
+TEST(SymbolParseHelper, ParseFunctionInvalid) {
+ uint64_t address;
+ uint64_t size;
+ long stack_param_size;
+ char *name;
+
+ // Test missing function name.
+ char kTestLine[] = "FUNC 1 2 3 ";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine, &address, &size,
+ &stack_param_size, &name));
+ // Test bad address.
+ char kTestLine1[] = "FUNC 1z 2 3 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine1, &address, &size,
+ &stack_param_size, &name));
+ // Test large address.
+ char kTestLine2[] = "FUNC 123123123123123123123123123 2 3 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine2, &address, &size,
+ &stack_param_size, &name));
+ // Test bad size.
+ char kTestLine3[] = "FUNC 1 z2 3 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine3, &address, &size,
+ &stack_param_size, &name));
+ // Test large size.
+ char kTestLine4[] = "FUNC 1 231231231231231231231231232 3 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine4, &address, &size,
+ &stack_param_size, &name));
+ // Test bad param size.
+ char kTestLine5[] = "FUNC 1 2 3z function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine5, &address, &size,
+ &stack_param_size, &name));
+ // Test large param size.
+ char kTestLine6[] = "FUNC 1 2 312312312312312312312312323 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine6, &address, &size,
+ &stack_param_size, &name));
+ // Negative param size.
+ char kTestLine7[] = "FUNC 1 2 -5 function name";
+ ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine7, &address, &size,
+ &stack_param_size, &name));
+}
+
+// Test parsing of valid lines. The format is:
+// <address> <size> <line number> <source file id>
+TEST(SymbolParseHelper, ParseLineValid) {
+ uint64_t address;
+ uint64_t size;
+ long line_number;
+ long source_file;
+
+ char kTestLine[] = "1 2 3 4";
+ ASSERT_TRUE(SymbolParseHelper::ParseLine(kTestLine, &address, &size,
+ &line_number, &source_file));
+ EXPECT_EQ(1ULL, address);
+ EXPECT_EQ(2ULL, size);
+ EXPECT_EQ(3, line_number);
+ EXPECT_EQ(4, source_file);
+
+ // Test hex size and address.
+ char kTestLine1[] = "a1 a2 3 4 // some comment";
+ ASSERT_TRUE(SymbolParseHelper::ParseLine(kTestLine1, &address, &size,
+ &line_number, &source_file));
+ EXPECT_EQ(0xa1ULL, address);
+ EXPECT_EQ(0xa2ULL, size);
+ EXPECT_EQ(3, line_number);
+ EXPECT_EQ(4, source_file);
+
+ // 0 is a valid line number.
+ char kTestLine2[] = "a1 a2 0 4 // some comment";
+ ASSERT_TRUE(SymbolParseHelper::ParseLine(kTestLine2, &address, &size,
+ &line_number, &source_file));
+ EXPECT_EQ(0xa1ULL, address);
+ EXPECT_EQ(0xa2ULL, size);
+ EXPECT_EQ(0, line_number);
+ EXPECT_EQ(4, source_file);
+}
+
+// Test parsing of invalid lines. The format is:
+// <address> <size> <line number> <source file id>
+TEST(SymbolParseHelper, ParseLineInvalid) {
+ uint64_t address;
+ uint64_t size;
+ long line_number;
+ long source_file;
+
+ // Test missing source file id.
+ char kTestLine[] = "1 2 3";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine, &address, &size,
+ &line_number, &source_file));
+ // Test bad address.
+ char kTestLine1[] = "1z 2 3 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine1, &address, &size,
+ &line_number, &source_file));
+ // Test large address.
+ char kTestLine2[] = "123123123123123123123123 2 3 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine2, &address, &size,
+ &line_number, &source_file));
+ // Test bad size.
+ char kTestLine3[] = "1 z2 3 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine3, &address, &size,
+ &line_number, &source_file));
+ // Test large size.
+ char kTestLine4[] = "1 123123123123123123123123 3 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine4, &address, &size,
+ &line_number, &source_file));
+ // Test bad line number.
+ char kTestLine5[] = "1 2 z3 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine5, &address, &size,
+ &line_number, &source_file));
+ // Test negative line number.
+ char kTestLine6[] = "1 2 -1 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine6, &address, &size,
+ &line_number, &source_file));
+ // Test large line number.
+ char kTestLine7[] = "1 2 123123123123123123123 4";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine7, &address, &size,
+ &line_number, &source_file));
+ // Test bad source file id.
+ char kTestLine8[] = "1 2 3 f";
+ ASSERT_FALSE(SymbolParseHelper::ParseLine(kTestLine8, &address, &size,
+ &line_number, &source_file));
+}
+
+// Test parsing of valid PUBLIC lines. The format is:
+// PUBLIC <address> <stack_param_size> <name>
+TEST(SymbolParseHelper, ParsePublicSymbolValid) {
+ uint64_t address;
+ long stack_param_size;
+ char *name;
+
+ char kTestLine[] = "PUBLIC 1 2 3";
+ ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &address,
+ &stack_param_size, &name));
+ EXPECT_EQ(1ULL, address);
+ EXPECT_EQ(2, stack_param_size);
+ EXPECT_EQ("3", string(name));
+
+ // Test hex size and address.
+ char kTestLine1[] = "PUBLIC a1 a2 function name";
+ ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &address,
+ &stack_param_size, &name));
+ EXPECT_EQ(0xa1ULL, address);
+ EXPECT_EQ(0xa2, stack_param_size);
+ EXPECT_EQ("function name", string(name));
+
+ // Test 0 is a valid address.
+ char kTestLine2[] = "PUBLIC 0 a2 function name";
+ ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &address,
+ &stack_param_size, &name));
+ EXPECT_EQ(0ULL, address);
+ EXPECT_EQ(0xa2, stack_param_size);
+ EXPECT_EQ("function name", string(name));
+}
+
+// Test parsing of invalid PUBLIC lines. The format is:
+// PUBLIC <address> <stack_param_size> <name>
+TEST(SymbolParseHelper, ParsePublicSymbolInvalid) {
+ uint64_t address;
+ long stack_param_size;
+ char *name;
+
+ // Test missing source function name.
+ char kTestLine[] = "PUBLIC 1 2 ";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &address,
+ &stack_param_size, &name));
+ // Test bad address.
+ char kTestLine1[] = "PUBLIC 1z 2 3";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &address,
+ &stack_param_size, &name));
+ // Test large address.
+ char kTestLine2[] = "PUBLIC 123123123123123123123123 2 3";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &address,
+ &stack_param_size, &name));
+ // Test bad param stack size.
+ char kTestLine3[] = "PUBLIC 1 z2 3";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine3, &address,
+ &stack_param_size, &name));
+ // Test large param stack size.
+ char kTestLine4[] = "PUBLIC 1 123123123123123123123123123 3";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine4, &address,
+ &stack_param_size, &name));
+ // Test negative param stack size.
+ char kTestLine5[] = "PUBLIC 1 -5 3";
+ ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine5, &address,
+ &stack_param_size, &name));
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/call_stack.cc b/toolkit/crashreporter/google-breakpad/src/processor/call_stack.cc
new file mode 100644
index 000000000..925f08469
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/call_stack.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// call_stack.cc: A call stack comprised of stack frames.
+//
+// See call_stack.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/stack_frame.h"
+
+namespace google_breakpad {
+
+CallStack::~CallStack() {
+ Clear();
+}
+
+void CallStack::Clear() {
+ for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
+ iterator != frames_.end();
+ ++iterator) {
+ delete *iterator;
+ }
+ tid_ = 0;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h
new file mode 100644
index 000000000..7e7af0af9
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h
@@ -0,0 +1,119 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions.
+
+#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_
+#define PROCESSOR_CFI_FRAME_INFO_INL_H_
+
+#include <string.h>
+
+namespace google_breakpad {
+
+template <typename RegisterType, class RawContextType>
+bool SimpleCFIWalker<RegisterType, RawContextType>::FindCallerRegisters(
+ const MemoryRegion &memory,
+ const CFIFrameInfo &cfi_frame_info,
+ const RawContextType &callee_context,
+ int callee_validity,
+ RawContextType *caller_context,
+ int *caller_validity) const {
+ typedef CFIFrameInfo::RegisterValueMap<RegisterType> ValueMap;
+ ValueMap callee_registers;
+ ValueMap caller_registers;
+ // Just for brevity.
+ typename ValueMap::const_iterator caller_none = caller_registers.end();
+
+ // Populate callee_registers with register values from callee_context.
+ for (size_t i = 0; i < map_size_; i++) {
+ const RegisterSet &r = register_map_[i];
+ if (callee_validity & r.validity_flag)
+ callee_registers[r.name] = callee_context.*r.context_member;
+ }
+
+ // Apply the rules, and see what register values they yield.
+ if (!cfi_frame_info.FindCallerRegs<RegisterType>(callee_registers, memory,
+ &caller_registers))
+ return false;
+
+ // Populate *caller_context with the values the rules placed in
+ // caller_registers.
+ memset(caller_context, 0xda, sizeof(*caller_context));
+ *caller_validity = 0;
+ for (size_t i = 0; i < map_size_; i++) {
+ const RegisterSet &r = register_map_[i];
+ typename ValueMap::const_iterator caller_entry;
+
+ // Did the rules provide a value for this register by its name?
+ caller_entry = caller_registers.find(r.name);
+ if (caller_entry != caller_none) {
+ caller_context->*r.context_member = caller_entry->second;
+ *caller_validity |= r.validity_flag;
+ continue;
+ }
+
+ // Did the rules provide a value for this register under its
+ // alternate name?
+ if (r.alternate_name) {
+ caller_entry = caller_registers.find(r.alternate_name);
+ if (caller_entry != caller_none) {
+ caller_context->*r.context_member = caller_entry->second;
+ *caller_validity |= r.validity_flag;
+ continue;
+ }
+ }
+
+ // Is this a callee-saves register? The walker assumes that these
+ // still hold the caller's value if the CFI doesn't mention them.
+ //
+ // Note that other frame walkers may fail to recover callee-saves
+ // registers; for example, the x86 "traditional" strategy only
+ // recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi
+ // are callee-saves, too. It is not correct to blindly set the
+ // valid bit for all callee-saves registers, without first
+ // checking its validity bit in the callee.
+ if (r.callee_saves && (callee_validity & r.validity_flag) != 0) {
+ caller_context->*r.context_member = callee_context.*r.context_member;
+ *caller_validity |= r.validity_flag;
+ continue;
+ }
+
+ // Otherwise, the register's value is unknown.
+ }
+
+ return true;
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc
new file mode 100644
index 000000000..0c4af7ba8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_frame_info.cc: Implementation of CFIFrameInfo class.
+// See cfi_frame_info.h for details.
+
+#include "processor/cfi_frame_info.h"
+
+#include <string.h>
+
+#include <sstream>
+
+#include "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))
+ return false;
+ (*caller_registers)[it->first] = value;
+ }
+
+ (*caller_registers)[".ra"] = ra;
+ (*caller_registers)[".cfa"] = cfa;
+
+ return true;
+}
+
+// Explicit instantiations for 32-bit and 64-bit architectures.
+template bool CFIFrameInfo::FindCallerRegs<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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h
new file mode 100644
index 000000000..90a1b3d74
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h
@@ -0,0 +1,275 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_frame_info.h: Define the CFIFrameInfo class, which holds the
+// set of 'STACK CFI'-derived register recovery rules that apply at a
+// given instruction.
+
+#ifndef PROCESSOR_CFI_FRAME_INFO_H_
+#define PROCESSOR_CFI_FRAME_INFO_H_
+
+#include <map>
+#include <string>
+
+#include "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_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc
new file mode 100644
index 000000000..542b28492
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc
@@ -0,0 +1,546 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo,
+// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker.
+
+#include <string.h>
+
+#include "breakpad_googletest_includes.h"
+#include "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_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
+ &caller_registers));
+}
+
+// Register rules can see the current frame's register values.
+TEST_F(Scope, RegsSeeRegs) {
+ ExpectNoMemoryReferences();
+
+ registers["$r1"] = 0x6ed3582c4bedb9adULL;
+ registers["$r2"] = 0xd27d9e742b8df6d0ULL;
+ cfi.SetCFARule("88239303");
+ cfi.SetRARule("30503835");
+ cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2");
+ cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1");
+ ASSERT_TRUE(cfi.FindCallerRegs<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");
+ 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"]);
+}
+
+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);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h
new file mode 100644
index 000000000..4c0ad41f9
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// contained_range_map-inl.h: Hierarchically-organized range map implementation.
+//
+// See contained_range_map.h for documentation.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
+#define PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
+
+#include "processor/contained_range_map.h"
+
+#include <assert.h>
+
+#include "processor/logging.h"
+
+
+namespace google_breakpad {
+
+
+template<typename AddressType, typename EntryType>
+ContainedRangeMap<AddressType, EntryType>::~ContainedRangeMap() {
+ // Clear frees the children pointed to by the map, and frees the map itself.
+ Clear();
+}
+
+
+template<typename AddressType, typename EntryType>
+bool ContainedRangeMap<AddressType, EntryType>::StoreRange(
+ const AddressType &base, const AddressType &size, const EntryType &entry) {
+ AddressType high = base + size - 1;
+
+ // Check for undersize or overflow.
+ if (size <= 0 || high < base) {
+ //TODO(nealsid) We are commenting this out in order to prevent
+ // excessive logging. We plan to move to better logging as this
+ // failure happens quite often and is expected(see comment in
+ // basic_source_line_resolver.cc:671).
+ // BPLOG(INFO) << "StoreRange failed, " << HexString(base) << "+"
+ // << HexString(size) << ", " << HexString(high);
+ return false;
+ }
+
+ if (!map_)
+ map_ = new AddressToRangeMap();
+
+ MapIterator iterator_base = map_->lower_bound(base);
+ MapIterator iterator_high = map_->lower_bound(high);
+ MapIterator iterator_end = map_->end();
+
+ if (iterator_base == iterator_high && iterator_base != iterator_end &&
+ base >= iterator_base->second->base_) {
+ // The new range is entirely within an existing child range.
+
+ // If the new range's geometry is exactly equal to an existing child
+ // range's, it violates the containment rules, and an attempt to store
+ // it must fail. iterator_base->first contains the key, which was the
+ // containing child's high address.
+ if (iterator_base->second->base_ == base && iterator_base->first == high) {
+ // TODO(nealsid): See the TODO above on why this is commented out.
+// BPLOG(INFO) << "StoreRange failed, identical range is already "
+// "present: " << HexString(base) << "+" << HexString(size);
+ return false;
+ }
+
+ // Pass the new range on to the child to attempt to store.
+ return iterator_base->second->StoreRange(base, size, entry);
+ }
+
+ // iterator_high might refer to an irrelevant range: one whose base address
+ // is higher than the new range's high address. Set contains_high to true
+ // only if iterator_high refers to a range that is at least partially
+ // within the new range.
+ bool contains_high = iterator_high != iterator_end &&
+ high >= iterator_high->second->base_;
+
+ // If the new range encompasses any existing child ranges, it must do so
+ // fully. Partial containment isn't allowed.
+ if ((iterator_base != iterator_end && base > iterator_base->second->base_) ||
+ (contains_high && high < iterator_high->first)) {
+ // TODO(mmentovai): Some symbol files will trip this check frequently
+ // on STACK lines. Too many messages will be produced. These are more
+ // suitable for a DEBUG channel than an INFO channel.
+ // BPLOG(INFO) << "StoreRange failed, new range partially contains "
+ // "existing range: " << HexString(base) << "+" <<
+ // HexString(size);
+ return false;
+ }
+
+ // When copying and erasing contained ranges, the "end" iterator needs to
+ // point one past the last item of the range to copy. If contains_high is
+ // false, the iterator's already in the right place; the increment is safe
+ // because contains_high can't be true if iterator_high == iterator_end.
+ if (contains_high)
+ ++iterator_high;
+
+ // Optimization: if the iterators are equal, no child ranges would be
+ // moved. Create the new child range with a NULL map to conserve space
+ // in leaf nodes, of which there will be many.
+ AddressToRangeMap *child_map = NULL;
+
+ if (iterator_base != iterator_high) {
+ // The children of this range that are contained by the new range must
+ // be transferred over to the new range. Create the new child range map
+ // and copy the pointers to range maps it should contain into it.
+ child_map = new AddressToRangeMap(iterator_base, iterator_high);
+
+ // Remove the copied child pointers from this range's map of children.
+ map_->erase(iterator_base, iterator_high);
+ }
+
+ // Store the new range in the map by its high address. Any children that
+ // the new child range contains were formerly children of this range but
+ // are now this range's grandchildren. Ownership of these is transferred
+ // to the new child range.
+ map_->insert(MapValue(high,
+ new ContainedRangeMap(base, entry, child_map)));
+ return true;
+}
+
+
+template<typename AddressType, typename EntryType>
+bool ContainedRangeMap<AddressType, EntryType>::RetrieveRange(
+ const AddressType &address, EntryType *entry) const {
+ BPLOG_IF(ERROR, !entry) << "ContainedRangeMap::RetrieveRange requires "
+ "|entry|";
+ assert(entry);
+
+ // If nothing was ever stored, then there's nothing to retrieve.
+ if (!map_)
+ return false;
+
+ // Get an iterator to the child range whose high address is equal to or
+ // greater than the supplied address. If the supplied address is higher
+ // than all of the high addresses in the range, then this range does not
+ // contain a child at address, so return false. If the supplied address
+ // is lower than the base address of the child range, then it is not within
+ // the child range, so return false.
+ MapConstIterator iterator = map_->lower_bound(address);
+ if (iterator == map_->end() || address < iterator->second->base_)
+ return false;
+
+ // The child in iterator->second contains the specified address. Find out
+ // if it has a more-specific descendant that also contains it. If it does,
+ // it will set |entry| appropriately. If not, set |entry| to the child.
+ if (!iterator->second->RetrieveRange(address, entry))
+ *entry = iterator->second->entry_;
+
+ return true;
+}
+
+
+template<typename AddressType, typename EntryType>
+void ContainedRangeMap<AddressType, EntryType>::Clear() {
+ if (map_) {
+ MapConstIterator end = map_->end();
+ for (MapConstIterator child = map_->begin(); child != end; ++child)
+ delete child->second;
+
+ delete map_;
+ map_ = NULL;
+ }
+}
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map.h b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map.h
new file mode 100644
index 000000000..1015ae8cf
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// contained_range_map.h: Hierarchically-organized range maps.
+//
+// A contained range map is similar to a standard range map, except it allows
+// objects to be organized hierarchically. A contained range map allows
+// objects to contain other objects. It is not sensitive to the order that
+// objects are added to the map: larger, more general, containing objects
+// may be added either before or after smaller, more specific, contained
+// ones.
+//
+// Contained range maps guarantee that each object may only contain smaller
+// objects than itself, and that a parent object may only contain child
+// objects located entirely within the parent's address space. Attempts
+// to introduce objects (via StoreRange) that violate these rules will fail.
+// Retrieval (via RetrieveRange) always returns the most specific (smallest)
+// object that contains the address being queried. Note that while it is
+// not possible to insert two objects into a map that have exactly the same
+// geometry (base address and size), it is possible to completely mask a
+// larger object by inserting smaller objects that entirely fill the larger
+// object's address space.
+//
+// Internally, contained range maps are implemented as a tree. Each tree
+// node except for the root node describes an object in the map. Each node
+// maintains its list of children in a map similar to a standard range map,
+// keyed by the highest address that each child occupies. Each node's
+// children occupy address ranges entirely within the node. The root node
+// is the only node directly accessible to the user, and represents the
+// entire address space.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_CONTAINED_RANGE_MAP_H__
+#define PROCESSOR_CONTAINED_RANGE_MAP_H__
+
+
+#include <map>
+
+
+namespace google_breakpad {
+
+// Forward declarations (for later friend declarations of specialized template).
+template<class, class> class ContainedRangeMapSerializer;
+
+template<typename AddressType, typename EntryType>
+class ContainedRangeMap {
+ public:
+ // The default constructor creates a ContainedRangeMap with no geometry
+ // and no entry, and as such is only suitable for the root node of a
+ // ContainedRangeMap tree.
+ ContainedRangeMap() : base_(), entry_(), map_(NULL) {}
+
+ ~ContainedRangeMap();
+
+ // Inserts a range into the map. If the new range is encompassed by
+ // an existing child range, the new range is passed into the child range's
+ // StoreRange method. If the new range encompasses any existing child
+ // ranges, those child ranges are moved to the new range, becoming
+ // grandchildren of this ContainedRangeMap. Returns false for a
+ // parameter error, or if the ContainedRangeMap hierarchy guarantees
+ // would be violated.
+ bool StoreRange(const AddressType &base,
+ const AddressType &size,
+ const EntryType &entry);
+
+ // Retrieves the most specific (smallest) descendant range encompassing
+ // the specified address. This method will only return entries held by
+ // child ranges, and not the entry contained by |this|. This is necessary
+ // to support a sparsely-populated root range. If no descendant range
+ // encompasses the address, returns false.
+ bool RetrieveRange(const AddressType &address, EntryType *entry) const;
+
+ // Removes all children. Note that Clear only removes descendants,
+ // leaving the node on which it is called intact. Because the only
+ // meaningful things contained by a root node are descendants, this
+ // is sufficient to restore an entire ContainedRangeMap to its initial
+ // empty state when called on the root node.
+ void Clear();
+
+ private:
+ friend class ContainedRangeMapSerializer<AddressType, EntryType>;
+ friend class ModuleComparer;
+
+ // AddressToRangeMap stores pointers. This makes reparenting simpler in
+ // StoreRange, because it doesn't need to copy entire objects.
+ typedef std::map<AddressType, ContainedRangeMap *> AddressToRangeMap;
+ typedef typename AddressToRangeMap::const_iterator MapConstIterator;
+ typedef typename AddressToRangeMap::iterator MapIterator;
+ typedef typename AddressToRangeMap::value_type MapValue;
+
+ // Creates a new ContainedRangeMap with the specified base address, entry,
+ // and initial child map, which may be NULL. This is only used internally
+ // by ContainedRangeMap when it creates a new child.
+ ContainedRangeMap(const AddressType &base, const EntryType &entry,
+ AddressToRangeMap *map)
+ : base_(base), entry_(entry), map_(map) {}
+
+ // The base address of this range. The high address does not need to
+ // be stored, because it is used as the key to an object in its parent's
+ // map, and all ContainedRangeMaps except for the root range are contained
+ // within maps. The root range does not actually contain an entry, so its
+ // base_ field is meaningless, and the fact that it has no parent and thus
+ // no key is unimportant. For this reason, the base_ field should only be
+ // is accessed on child ContainedRangeMap objects, and never on |this|.
+ const AddressType base_;
+
+ // The entry corresponding to this range. The root range does not
+ // actually contain an entry, so its entry_ field is meaningless. For
+ // this reason, the entry_ field should only be accessed on child
+ // ContainedRangeMap objects, and never on |this|.
+ const EntryType entry_;
+
+ // The map containing child ranges, keyed by each child range's high
+ // address. This is a pointer to avoid allocating map structures for
+ // leaf nodes, where they are not needed.
+ AddressToRangeMap *map_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map_unittest.cc
new file mode 100644
index 000000000..e5910da0d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// contained_range_map_unittest.cc: Unit tests for ContainedRangeMap
+//
+// Author: Mark Mentovai
+
+#include <stdio.h>
+
+#include "processor/contained_range_map-inl.h"
+
+#include "processor/logging.h"
+
+
+#define ASSERT_TRUE(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
+ return false; \
+ }
+
+#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
+
+
+namespace {
+
+
+using google_breakpad::ContainedRangeMap;
+
+
+static bool RunTests() {
+ ContainedRangeMap<unsigned int, int> crm;
+
+ // First, do the StoreRange tests. This validates the containment
+ // rules.
+ ASSERT_TRUE (crm.StoreRange(10, 10, 1));
+ ASSERT_FALSE(crm.StoreRange(10, 10, 2)); // exactly equal to 1
+ ASSERT_FALSE(crm.StoreRange(11, 10, 3)); // begins inside 1 and extends up
+ ASSERT_FALSE(crm.StoreRange( 9, 10, 4)); // begins below 1 and ends inside
+ ASSERT_TRUE (crm.StoreRange(11, 9, 5)); // contained by existing
+ ASSERT_TRUE (crm.StoreRange(12, 7, 6));
+ ASSERT_TRUE (crm.StoreRange( 9, 12, 7)); // contains existing
+ ASSERT_TRUE (crm.StoreRange( 9, 13, 8));
+ ASSERT_TRUE (crm.StoreRange( 8, 14, 9));
+ ASSERT_TRUE (crm.StoreRange(30, 3, 10));
+ ASSERT_TRUE (crm.StoreRange(33, 3, 11));
+ ASSERT_TRUE (crm.StoreRange(30, 6, 12)); // storable but totally masked
+ ASSERT_TRUE (crm.StoreRange(40, 8, 13)); // will be totally masked
+ ASSERT_TRUE (crm.StoreRange(40, 4, 14));
+ ASSERT_TRUE (crm.StoreRange(44, 4, 15));
+ ASSERT_FALSE(crm.StoreRange(32, 10, 16)); // begins in #10, ends in #14
+ ASSERT_FALSE(crm.StoreRange(50, 0, 17)); // zero length
+ ASSERT_TRUE (crm.StoreRange(50, 10, 18));
+ ASSERT_TRUE (crm.StoreRange(50, 1, 19));
+ ASSERT_TRUE (crm.StoreRange(59, 1, 20));
+ ASSERT_TRUE (crm.StoreRange(60, 1, 21));
+ ASSERT_TRUE (crm.StoreRange(69, 1, 22));
+ ASSERT_TRUE (crm.StoreRange(60, 10, 23));
+ ASSERT_TRUE (crm.StoreRange(68, 1, 24));
+ ASSERT_TRUE (crm.StoreRange(61, 1, 25));
+ ASSERT_TRUE (crm.StoreRange(61, 8, 26));
+ ASSERT_FALSE(crm.StoreRange(59, 9, 27));
+ ASSERT_FALSE(crm.StoreRange(59, 10, 28));
+ ASSERT_FALSE(crm.StoreRange(59, 11, 29));
+ ASSERT_TRUE (crm.StoreRange(70, 10, 30));
+ ASSERT_TRUE (crm.StoreRange(74, 2, 31));
+ ASSERT_TRUE (crm.StoreRange(77, 2, 32));
+ ASSERT_FALSE(crm.StoreRange(72, 6, 33));
+ ASSERT_TRUE (crm.StoreRange(80, 3, 34));
+ ASSERT_TRUE (crm.StoreRange(81, 1, 35));
+ ASSERT_TRUE (crm.StoreRange(82, 1, 36));
+ ASSERT_TRUE (crm.StoreRange(83, 3, 37));
+ ASSERT_TRUE (crm.StoreRange(84, 1, 38));
+ ASSERT_TRUE (crm.StoreRange(83, 1, 39));
+ ASSERT_TRUE (crm.StoreRange(86, 5, 40));
+ ASSERT_TRUE (crm.StoreRange(88, 1, 41));
+ ASSERT_TRUE (crm.StoreRange(90, 1, 42));
+ ASSERT_TRUE (crm.StoreRange(86, 1, 43));
+ ASSERT_TRUE (crm.StoreRange(87, 1, 44));
+ ASSERT_TRUE (crm.StoreRange(89, 1, 45));
+ ASSERT_TRUE (crm.StoreRange(87, 4, 46));
+ ASSERT_TRUE (crm.StoreRange(87, 3, 47));
+ ASSERT_FALSE(crm.StoreRange(86, 2, 48));
+
+ // Each element in test_data contains the expected result when calling
+ // RetrieveRange on an address.
+ const int test_data[] = {
+ 0, // 0
+ 0, // 1
+ 0, // 2
+ 0, // 3
+ 0, // 4
+ 0, // 5
+ 0, // 6
+ 0, // 7
+ 9, // 8
+ 7, // 9
+ 1, // 10
+ 5, // 11
+ 6, // 12
+ 6, // 13
+ 6, // 14
+ 6, // 15
+ 6, // 16
+ 6, // 17
+ 6, // 18
+ 5, // 19
+ 7, // 20
+ 8, // 21
+ 0, // 22
+ 0, // 23
+ 0, // 24
+ 0, // 25
+ 0, // 26
+ 0, // 27
+ 0, // 28
+ 0, // 29
+ 10, // 30
+ 10, // 31
+ 10, // 32
+ 11, // 33
+ 11, // 34
+ 11, // 35
+ 0, // 36
+ 0, // 37
+ 0, // 38
+ 0, // 39
+ 14, // 40
+ 14, // 41
+ 14, // 42
+ 14, // 43
+ 15, // 44
+ 15, // 45
+ 15, // 46
+ 15, // 47
+ 0, // 48
+ 0, // 49
+ 19, // 50
+ 18, // 51
+ 18, // 52
+ 18, // 53
+ 18, // 54
+ 18, // 55
+ 18, // 56
+ 18, // 57
+ 18, // 58
+ 20, // 59
+ 21, // 60
+ 25, // 61
+ 26, // 62
+ 26, // 63
+ 26, // 64
+ 26, // 65
+ 26, // 66
+ 26, // 67
+ 24, // 68
+ 22, // 69
+ 30, // 70
+ 30, // 71
+ 30, // 72
+ 30, // 73
+ 31, // 74
+ 31, // 75
+ 30, // 76
+ 32, // 77
+ 32, // 78
+ 30, // 79
+ 34, // 80
+ 35, // 81
+ 36, // 82
+ 39, // 83
+ 38, // 84
+ 37, // 85
+ 43, // 86
+ 44, // 87
+ 41, // 88
+ 45, // 89
+ 42, // 90
+ 0, // 91
+ 0, // 92
+ 0, // 93
+ 0, // 94
+ 0, // 95
+ 0, // 96
+ 0, // 97
+ 0, // 98
+ 0 // 99
+ };
+ unsigned int test_high = sizeof(test_data) / sizeof(int);
+
+ // Now, do the RetrieveRange tests. This further validates that the
+ // objects were stored properly and that retrieval returns the correct
+ // object.
+ // If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a
+ // new test_data array will be printed. Exercise caution when doing this.
+ // Be sure to verify the results manually!
+#ifdef GENERATE_TEST_DATA
+ printf(" const int test_data[] = {\n");
+#endif // GENERATE_TEST_DATA
+
+ for (unsigned int address = 0; address < test_high; ++address) {
+ int value;
+ if (!crm.RetrieveRange(address, &value))
+ value = 0;
+
+#ifndef GENERATE_TEST_DATA
+ // Don't use ASSERT inside the loop because it won't show the failed
+ // |address|, and the line number will always be the same. That makes
+ // it difficult to figure out which test failed.
+ if (value != test_data[address]) {
+ fprintf(stderr, "FAIL: retrieve %d expected %d observed %d @ %s:%d\n",
+ address, test_data[address], value, __FILE__, __LINE__);
+ return false;
+ }
+#else // !GENERATE_TEST_DATA
+ printf(" %d%c%s // %d\n", value,
+ address == test_high - 1 ? ' ' : ',',
+ value < 10 ? " " : "",
+ address);
+#endif // !GENERATE_TEST_DATA
+ }
+
+#ifdef GENERATE_TEST_DATA
+ printf(" };\n");
+#endif // GENERATE_TEST_DATA
+
+ return true;
+}
+
+
+} // namespace
+
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ return RunTests() ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc
new file mode 100644
index 000000000..559022404
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc
@@ -0,0 +1,240 @@
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// disassembler_x86.cc: simple x86 disassembler.
+//
+// Provides single step disassembly of x86 bytecode and flags instructions
+// that utilize known bad register values.
+//
+// Author: Cris Neckar
+
+#include "processor/disassembler_x86.h"
+
+#include <string.h>
+
+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(&current_instr_);
+
+ libdis::x86_cleanup();
+}
+
+uint32_t DisassemblerX86::NextInstruction() {
+ if (instr_valid_)
+ libdis::x86_oplist_free(&current_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_,
+ &current_instr_);
+ if (instr_size == 0) {
+ instr_valid_ = false;
+ return 0;
+ }
+
+ current_byte_offset_ += instr_size;
+ current_inst_offset_++;
+ instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
+ if (!instr_valid_)
+ return 0;
+
+ if (current_instr_.type == libdis::insn_return)
+ end_of_block_ = true;
+ libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
+ libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
+
+ if (register_valid_) {
+ switch (current_instr_.group) {
+ // Flag branches based off of bad registers and calls that occur
+ // after pushing bad values.
+ case libdis::insn_controlflow:
+ switch (current_instr_.type) {
+ case libdis::insn_jmp:
+ case libdis::insn_jcc:
+ case libdis::insn_call:
+ case libdis::insn_callcc:
+ if (dest) {
+ switch (dest->type) {
+ case libdis::op_expression:
+ if (dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BRANCH_TARGET;
+ break;
+ case libdis::op_register:
+ if (dest->data.reg.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BRANCH_TARGET;
+ break;
+ default:
+ if (pushed_bad_value_ &&
+ (current_instr_.type == libdis::insn_call ||
+ current_instr_.type == libdis::insn_callcc))
+ flags_ |= DISX86_BAD_ARGUMENT_PASSED;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ // Flag block data operations that use bad registers for src or dest.
+ case libdis::insn_string:
+ if (dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BLOCK_WRITE;
+ if (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_BLOCK_READ;
+ break;
+
+ // Flag comparisons based on bad data.
+ case libdis::insn_comparison:
+ if ((dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id) ||
+ (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id) ||
+ (dest && dest->type == libdis::op_register &&
+ dest->data.reg.id == bad_register_.id) ||
+ (src && src->type == libdis::op_register &&
+ src->data.reg.id == bad_register_.id))
+ flags_ |= DISX86_BAD_COMPARISON;
+ break;
+
+ // Flag any other instruction which derefs a bad register for
+ // src or dest.
+ default:
+ if (dest && dest->type == libdis::op_expression &&
+ dest->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_WRITE;
+ if (src && src->type == libdis::op_expression &&
+ src->data.expression.base.id == bad_register_.id)
+ flags_ |= DISX86_BAD_READ;
+ break;
+ }
+ }
+
+ // When a register is marked as tainted check if it is pushed.
+ // TODO(cdn): may also want to check for MOVs into EBP offsets.
+ if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
+ switch (dest->type) {
+ case libdis::op_expression:
+ if (dest->data.expression.base.id == bad_register_.id ||
+ dest->data.expression.index.id == bad_register_.id)
+ pushed_bad_value_ = true;
+ break;
+ case libdis::op_register:
+ if (dest->data.reg.id == bad_register_.id)
+ pushed_bad_value_ = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Check if a tainted register value is clobbered.
+ // For conditional MOVs and XCHGs assume that
+ // there is a hit.
+ if (register_valid_) {
+ switch (current_instr_.type) {
+ case libdis::insn_xor:
+ if (src && src->type == libdis::op_register &&
+ dest && dest->type == libdis::op_register &&
+ src->data.reg.id == bad_register_.id &&
+ src->data.reg.id == dest->data.reg.id)
+ register_valid_ = false;
+ break;
+ case libdis::insn_pop:
+ case libdis::insn_mov:
+ case libdis::insn_movcc:
+ if (dest && dest->type == libdis::op_register &&
+ dest->data.reg.id == bad_register_.id)
+ register_valid_ = false;
+ break;
+ case libdis::insn_popregs:
+ register_valid_ = false;
+ break;
+ case libdis::insn_xchg:
+ case libdis::insn_xchgcc:
+ if (dest && dest->type == libdis::op_register &&
+ src && src->type == libdis::op_register) {
+ if (dest->data.reg.id == bad_register_.id)
+ memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
+ else if (src->data.reg.id == bad_register_.id)
+ memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return instr_size;
+}
+
+bool DisassemblerX86::setBadRead() {
+ if (!instr_valid_)
+ return false;
+
+ libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
+ if (!operand || operand->type != libdis::op_expression)
+ return false;
+
+ memcpy(&bad_register_, &operand->data.expression.base,
+ sizeof(libdis::x86_reg_t));
+ register_valid_ = true;
+ return true;
+}
+
+bool DisassemblerX86::setBadWrite() {
+ if (!instr_valid_)
+ return false;
+
+ libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
+ if (!operand || operand->type != libdis::op_expression)
+ return false;
+
+ memcpy(&bad_register_, &operand->data.expression.base,
+ sizeof(libdis::x86_reg_t));
+ register_valid_ = true;
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.h b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.h
new file mode 100644
index 000000000..710694107
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.h
@@ -0,0 +1,127 @@
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// disassembler_x86.h: Basic x86 bytecode disassembler
+//
+// Provides a simple disassembler which wraps libdisasm. This allows simple
+// tests to be run against bytecode to test for various properties.
+//
+// Author: Cris Neckar
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_
+#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_
+
+#include <stddef.h>
+#include <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_ ? &current_instr_ : NULL;
+ }
+
+ // Returns the type of the current instruction as defined in libdis.h.
+ libdis::x86_insn_group currentInstructionGroup() {
+ return current_instr_.group;
+ }
+
+ // Indicates whether a return instruction has been encountered.
+ bool endOfBlock() { return end_of_block_; }
+
+ // The flags set so far for the disassembly.
+ 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_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86_unittest.cc
new file mode 100644
index 000000000..352905f20
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86_unittest.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+
+#include <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());
+}
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/dump_context.cc b/toolkit/crashreporter/google-breakpad/src/processor/dump_context.cc
new file mode 100644
index 000000000..762d4fe21
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/dump_context.cc
@@ -0,0 +1,659 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// dump_context.cc: A (mini/micro)dump context.
+//
+// See dump_context.h for documentation.
+
+#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;
+}
+
+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;
+ 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;
+ 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::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;
+
+ 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();
+ 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(" iregs[%2d] = 0x%x\n",
+ 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%" PRIx64 "\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;
+ }
+
+ default: {
+ break;
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/dump_object.cc b/toolkit/crashreporter/google-breakpad/src/processor/dump_object.cc
new file mode 100644
index 000000000..2c82b200b
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/dump_object.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// dump_object.cc: A base class for all mini/micro dump object.
+
+#include "google_breakpad/processor/dump_object.h"
+
+namespace google_breakpad {
+
+DumpObject::DumpObject() : valid_(false) {
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability.cc b/toolkit/crashreporter/google-breakpad/src/processor/exploitability.cc
new file mode 100644
index 000000000..6ee1e9622
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// exploitability_engine.cc: Generic exploitability engine.
+//
+// See exploitable_engine.h for documentation.
+//
+// Author: Cris Neckar
+
+
+#include <cassert>
+
+#include "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:
+ 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
+
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.cc b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.cc
new file mode 100644
index 000000000..63056c438
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.cc
@@ -0,0 +1,625 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// exploitability_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
+
+#include "processor/exploitability_linux.h"
+
+#ifndef _WIN32
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sstream>
+#include <iterator>
+#endif // _WIN32
+
+#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"
+#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";
+
+#ifndef _WIN32
+const unsigned int MAX_INSTRUCTION_LEN = 15;
+const unsigned int MAX_OBJDUMP_BUFFER_LEN = 4096;
+#endif // _WIN32
+
+} // 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) {
+#ifdef _WIN32
+ BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method.";
+#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;
+ }
+ // Check architecture and set architecture variable to corresponding flag
+ // in objdump.
+ switch (context->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ architecture = "i386";
+ break;
+ case MD_CONTEXT_AMD64:
+ architecture = "i386:x86-64";
+ break;
+ default:
+ // Unsupported architecture. Note that ARM architectures are not
+ // supported because objdump does not support ARM.
+ return false;
+ break;
+ }
+
+ // Get memory region around instruction pointer and the number of bytes
+ // before and after the instruction pointer in the memory region.
+ const uint8_t *raw_memory = memory_region->GetMemory();
+ const uint64_t base = memory_region->GetBase();
+ if (base > instruction_ptr) {
+ BPLOG(ERROR) << "Memory region base value exceeds instruction pointer.";
+ return false;
+ }
+ const uint64_t offset = instruction_ptr - base;
+ if (memory_region->GetSize() < MAX_INSTRUCTION_LEN + offset) {
+ BPLOG(INFO) << "Not enough bytes left to guarantee complete instruction.";
+ return false;
+ }
+
+ // Convert bytes into objdump output.
+ char objdump_output_buffer[MAX_OBJDUMP_BUFFER_LEN] = {0};
+ DisassembleBytes(architecture,
+ raw_memory + offset,
+ MAX_OBJDUMP_BUFFER_LEN,
+ objdump_output_buffer);
+
+ string line;
+ if (!GetObjdumpInstructionLine(objdump_output_buffer, &line)) {
+ return false;
+ }
+
+ // Convert objdump instruction line into the operation and operands.
+ string instruction = "";
+ string dest = "";
+ string src = "";
+ TokenizeObjdumpInstruction(line, &instruction, &dest, &src);
+
+ // Check if the operation is a write to memory. First, the instruction
+ // must one that can write to memory. Second, the write destination
+ // must be a spot in memory rather than a register. Since there are no
+ // symbols from objdump, the destination will be enclosed by brackets.
+ if (dest.size() > 2 && dest.at(0) == '[' && dest.at(dest.size() - 1) == ']' &&
+ (!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"))) {
+ // Strip away enclosing brackets from the destination address.
+ dest = dest.substr(1, dest.size() - 2);
+ uint64_t write_address = 0;
+ CalculateAddress(dest, *context, &write_address);
+
+ // 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;
+ }
+#endif // _WIN32
+ return false;
+}
+
+#ifndef _WIN32
+bool ExploitabilityLinux::CalculateAddress(const string &address_expression,
+ const DumpContext &context,
+ uint64_t *write_address) {
+ // The destination should be the format reg+a or reg-a, where reg
+ // is a register and a is a hexadecimal constant. Although more complex
+ // expressions can make valid instructions, objdump's disassembly outputs
+ // it in this simpler format.
+ // TODO(liuandrew): Handle more complex formats, should they arise.
+
+ if (!write_address) {
+ BPLOG(ERROR) << "Null parameter.";
+ return false;
+ }
+
+ // Clone parameter into a non-const string.
+ string expression = address_expression;
+
+ // Parse out the constant that is added to the address (if it exists).
+ size_t delim = expression.find('+');
+ bool positive_add_constant = true;
+ // Check if constant is subtracted instead of added.
+ if (delim == string::npos) {
+ positive_add_constant = false;
+ delim = expression.find('-');
+ }
+ uint32_t add_constant = 0;
+ // Save constant and remove it from the expression.
+ if (delim != string::npos) {
+ if (!sscanf(expression.substr(delim + 1).c_str(), "%x", &add_constant)) {
+ BPLOG(ERROR) << "Failed to scan constant.";
+ return false;
+ }
+ expression = expression.substr(0, delim);
+ }
+
+ // Set the the write address to the corresponding register.
+ // TODO(liuandrew): Add support for partial registers, such as
+ // the rax/eax/ax/ah/al chain.
+ switch (context.GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ if (!expression.compare("eax")) {
+ *write_address = context.GetContextX86()->eax;
+ } else if (!expression.compare("ebx")) {
+ *write_address = context.GetContextX86()->ebx;
+ } else if (!expression.compare("ecx")) {
+ *write_address = context.GetContextX86()->ecx;
+ } else if (!expression.compare("edx")) {
+ *write_address = context.GetContextX86()->edx;
+ } else if (!expression.compare("edi")) {
+ *write_address = context.GetContextX86()->edi;
+ } else if (!expression.compare("esi")) {
+ *write_address = context.GetContextX86()->esi;
+ } else if (!expression.compare("ebp")) {
+ *write_address = context.GetContextX86()->ebp;
+ } else if (!expression.compare("esp")) {
+ *write_address = context.GetContextX86()->esp;
+ } else if (!expression.compare("eip")) {
+ *write_address = context.GetContextX86()->eip;
+ } else {
+ BPLOG(ERROR) << "Unsupported register";
+ return false;
+ }
+ break;
+ case MD_CONTEXT_AMD64:
+ if (!expression.compare("rax")) {
+ *write_address = context.GetContextAMD64()->rax;
+ } else if (!expression.compare("rbx")) {
+ *write_address = context.GetContextAMD64()->rbx;
+ } else if (!expression.compare("rcx")) {
+ *write_address = context.GetContextAMD64()->rcx;
+ } else if (!expression.compare("rdx")) {
+ *write_address = context.GetContextAMD64()->rdx;
+ } else if (!expression.compare("rdi")) {
+ *write_address = context.GetContextAMD64()->rdi;
+ } else if (!expression.compare("rsi")) {
+ *write_address = context.GetContextAMD64()->rsi;
+ } else if (!expression.compare("rbp")) {
+ *write_address = context.GetContextAMD64()->rbp;
+ } else if (!expression.compare("rsp")) {
+ *write_address = context.GetContextAMD64()->rsp;
+ } else if (!expression.compare("rip")) {
+ *write_address = context.GetContextAMD64()->rip;
+ } else if (!expression.compare("r8")) {
+ *write_address = context.GetContextAMD64()->r8;
+ } else if (!expression.compare("r9")) {
+ *write_address = context.GetContextAMD64()->r9;
+ } else if (!expression.compare("r10")) {
+ *write_address = context.GetContextAMD64()->r10;
+ } else if (!expression.compare("r11")) {
+ *write_address = context.GetContextAMD64()->r11;
+ } else if (!expression.compare("r12")) {
+ *write_address = context.GetContextAMD64()->r12;
+ } else if (!expression.compare("r13")) {
+ *write_address = context.GetContextAMD64()->r13;
+ } else if (!expression.compare("r14")) {
+ *write_address = context.GetContextAMD64()->r14;
+ } else if (!expression.compare("r15")) {
+ *write_address = context.GetContextAMD64()->r15;
+ } else {
+ BPLOG(ERROR) << "Unsupported register";
+ return false;
+ }
+ break;
+ default:
+ // This should not occur since the same switch condition
+ // should have terminated this method.
+ return false;
+ break;
+ }
+
+ // Add or subtract constant from write address (if applicable).
+ *write_address =
+ positive_add_constant ?
+ *write_address + add_constant : *write_address - add_constant;
+
+ return true;
+}
+
+// static
+bool ExploitabilityLinux::GetObjdumpInstructionLine(
+ const char *objdump_output_buffer,
+ string *instruction_line) {
+ // Put buffer data into stream to output line-by-line.
+ std::stringstream objdump_stream;
+ objdump_stream.str(string(objdump_output_buffer));
+
+ // Pipe each output line into the string until the string contains the first
+ // instruction from objdump. All lines before the "<.data>:" section are
+ // skipped. Loop until the line shows the first instruction or there are no
+ // lines left.
+ bool data_section_seen = false;
+ do {
+ if (!getline(objdump_stream, *instruction_line)) {
+ BPLOG(INFO) << "Objdump instructions not found";
+ return false;
+ }
+ if (instruction_line->find("<.data>:") != string::npos) {
+ data_section_seen = true;
+ }
+ } while (!data_section_seen || instruction_line->find("0:") == string::npos);
+ // This first instruction contains the above substring.
+
+ return true;
+}
+
+bool ExploitabilityLinux::TokenizeObjdumpInstruction(const string &line,
+ string *operation,
+ string *dest,
+ string *src) {
+ if (!operation || !dest || !src) {
+ BPLOG(ERROR) << "Null parameters passed.";
+ return false;
+ }
+
+ // Set all pointer values to empty strings.
+ *operation = "";
+ *dest = "";
+ *src = "";
+
+ // Tokenize the objdump line.
+ vector<string> tokens;
+ std::istringstream line_stream(line);
+ copy(std::istream_iterator<string>(line_stream),
+ std::istream_iterator<string>(),
+ std::back_inserter(tokens));
+
+ // Regex for the data in hex form. Each byte is two hex digits.
+ regex_t regex;
+ regcomp(&regex, "^[[:xdigit:]]{2}$", REG_EXTENDED | REG_NOSUB);
+
+ // Find and set the location of the operator. The operator appears
+ // directly after the chain of bytes that define the instruction. The
+ // operands will be the last token, given that the instruction has operands.
+ // If not, the operator is the last token. The loop skips the first token
+ // because the first token is the instruction number (namely "0:").
+ string operands = "";
+ for (size_t i = 1; i < tokens.size(); i++) {
+ // Check if current token no longer is in byte format.
+ if (regexec(&regex, tokens[i].c_str(), 0, NULL, 0)) {
+ // instruction = tokens[i];
+ *operation = tokens[i];
+ // If the operator is the last token, there are no operands.
+ if (i != tokens.size() - 1) {
+ operands = tokens[tokens.size() - 1];
+ }
+ break;
+ }
+ }
+ regfree(&regex);
+
+ if (operation->empty()) {
+ BPLOG(ERROR) << "Failed to parse out operation from objdump instruction.";
+ return false;
+ }
+
+ // Split operands into source and destination (if applicable).
+ if (!operands.empty()) {
+ size_t delim = operands.find(',');
+ if (delim == string::npos) {
+ *dest = operands;
+ } else {
+ *dest = operands.substr(0, delim);
+ *src = operands.substr(delim + 1);
+ }
+ }
+ return true;
+}
+
+bool ExploitabilityLinux::DisassembleBytes(const string &architecture,
+ const uint8_t *raw_bytes,
+ const unsigned int buffer_len,
+ char *objdump_output_buffer) {
+ if (!raw_bytes || !objdump_output_buffer) {
+ BPLOG(ERROR) << "Bad input parameters.";
+ return false;
+ }
+
+ // Write raw bytes around instruction pointer to a temporary file to
+ // pass as an argument to objdump.
+ char raw_bytes_tmpfile[] = "/tmp/breakpad_mem_region-raw_bytes-XXXXXX";
+ int raw_bytes_fd = mkstemp(raw_bytes_tmpfile);
+ if (raw_bytes_fd < 0) {
+ BPLOG(ERROR) << "Failed to create tempfile.";
+ unlink(raw_bytes_tmpfile);
+ return false;
+ }
+ if (write(raw_bytes_fd, raw_bytes, MAX_INSTRUCTION_LEN)
+ != MAX_INSTRUCTION_LEN) {
+ BPLOG(ERROR) << "Writing of raw bytes failed.";
+ unlink(raw_bytes_tmpfile);
+ return false;
+ }
+
+ char cmd[1024] = {0};
+ snprintf(cmd,
+ 1024,
+ "objdump -D -b binary -M intel -m %s %s",
+ architecture.c_str(),
+ raw_bytes_tmpfile);
+ FILE *objdump_fp = popen(cmd, "r");
+ if (!objdump_fp) {
+ fclose(objdump_fp);
+ unlink(raw_bytes_tmpfile);
+ BPLOG(ERROR) << "Failed to call objdump.";
+ return false;
+ }
+ if (fread(objdump_output_buffer, 1, buffer_len, objdump_fp) <= 0) {
+ fclose(objdump_fp);
+ unlink(raw_bytes_tmpfile);
+ BPLOG(ERROR) << "Failed to read objdump output.";
+ return false;
+ }
+ fclose(objdump_fp);
+ unlink(raw_bytes_tmpfile);
+ return true;
+}
+#endif // _WIN32
+
+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;
+ break;
+ default:
+ return false;
+ break;
+ }
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.h b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.h
new file mode 100644
index 000000000..e3ff13b6e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_linux.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// exploitability_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);
+
+#ifndef _WIN32
+ // Disassembles raw bytes via objdump and pipes the output into the provided
+ // buffer, given the desired architecture, the file from which objdump will
+ // read, and the buffer length. The method returns whether the disassembly
+ // was a success, and the caller owns all pointers.
+ static bool DisassembleBytes(const string &architecture,
+ const uint8_t *raw_bytes,
+ const unsigned int MAX_OBJDUMP_BUFFER_LEN,
+ char *objdump_output_buffer);
+
+ // Parses the objdump output given in |objdump_output_buffer| and extracts
+ // the line of the first instruction into |instruction_line|. Returns true
+ // when the instruction line is successfully extracted.
+ static bool GetObjdumpInstructionLine(
+ const char *objdump_output_buffer,
+ string *instruction_line);
+
+ // Tokenizes out the operation and operands from a line of instruction
+ // disassembled by objdump. This method modifies the pointers to match the
+ // tokens of the instruction, and returns if the tokenizing was a success.
+ // The caller owns all pointers.
+ static bool TokenizeObjdumpInstruction(const string &line,
+ string *operation,
+ string *dest,
+ string *src);
+
+ // Calculates the effective address of an expression in the form reg+a or
+ // reg-a, where 'reg' is a register and 'a' is a constant, and writes the
+ // result in the pointer. The method returns whether the calculation was
+ // a success. The caller owns the pointer.
+ static bool CalculateAddress(const string &address_expression,
+ const DumpContext &context,
+ uint64_t *write_address);
+#endif // _WIN32
+
+ // 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_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_unittest.cc
new file mode 100644
index 000000000..528ee5f21
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_unittest.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#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"
+#ifndef _WIN32
+#include "processor/exploitability_linux.h"
+#endif // _WIN32
+#include "processor/simple_symbol_supplier.h"
+
+#ifndef _WIN32
+namespace google_breakpad {
+
+class ExploitabilityLinuxTest : public ExploitabilityLinux {
+ public:
+ using ExploitabilityLinux::CalculateAddress;
+ using ExploitabilityLinux::DisassembleBytes;
+ using ExploitabilityLinux::GetObjdumpInstructionLine;
+ using ExploitabilityLinux::TokenizeObjdumpInstruction;
+};
+
+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 // _WIN32
+
+namespace {
+
+using google_breakpad::BasicSourceLineResolver;
+#ifndef _WIN32
+using google_breakpad::ExploitabilityLinuxTest;
+using google_breakpad::ExploitabilityLinuxTestMinidumpContext;
+#endif // _WIN32
+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(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"));
+#ifndef _WIN32
+ 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 // _WIN32
+}
+
+#ifndef _WIN32
+TEST(ExploitabilityLinuxUtilsTest, DisassembleBytesTest) {
+ ASSERT_FALSE(ExploitabilityLinuxTest::DisassembleBytes("", NULL, 5, NULL));
+ uint8_t bytes[6] = {0xc7, 0x0, 0x5, 0x0, 0x0, 0x0};
+ char buffer[1024] = {0};
+ ASSERT_TRUE(ExploitabilityLinuxTest::DisassembleBytes("i386:x86-64",
+ bytes,
+ 1024,
+ buffer));
+ std::stringstream objdump_stream;
+ objdump_stream.str(string(buffer));
+ string line = "";
+ while (line.find("<.data>") == string::npos)
+ getline(objdump_stream, line);
+ getline(objdump_stream, line);
+ ASSERT_EQ(line, " 0:\tc7 00 05 00 00 00 \tmov DWORD PTR [rax],0x5");
+}
+
+TEST(ExploitabilityLinuxUtilsTest, GetObjdumpInstructionLine) {
+ string disassebly =
+ "\n"
+ "/tmp/breakpad_mem_region-raw_bytes-tMmMo0: file format binary\n"
+ "// Trying to confuse the parser 0:\n"
+ "\n"
+ "Disassembly of section .data:\n"
+ "\n"
+ "0000000000000000 <.data>:\n"
+ " 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1\n"
+ " 6:\t5d \tpop rbp\n"
+ " 7:\tc3 \tret \n"
+ " 8:\t55 \tpush rbp\n"
+ " 9:\t48 89 e5 \tmov rbp,rsp\n"
+ " c:\t53 \tpush rbx\n"
+ " d:\t48 \trex.W\n"
+ " e:\t81 \t.byte 0x81\n";
+ string line;
+ EXPECT_TRUE(ExploitabilityLinuxTest::GetObjdumpInstructionLine(
+ disassebly.c_str(), &line));
+ EXPECT_EQ(" 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1", line);
+
+ // There is no "0:" after "<.data>:". Expected to return false.
+ disassebly =
+ "\n"
+ "/tmp/breakpad_mem_region-raw_bytes-tMmMo0: file format binary\n"
+ "// Trying to confuse the parser 0:\n"
+ "\n"
+ "Disassembly of section .data:\n"
+ "\n"
+ " 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1\n"
+ " 6:\t5d \tpop rbp\n"
+ " 7:\tc3 \tret \n"
+ " 8:\t55 \tpush rbp\n"
+ " 9:\t48 89 e5 \tmov rbp,rsp\n"
+ " d:\t48 \trex.W\n"
+ "0000000000000000 <.data>:\n"
+ " c:\t53 \tpush rbx\n";
+ EXPECT_FALSE(ExploitabilityLinuxTest::GetObjdumpInstructionLine(
+ disassebly.c_str(), &line));
+}
+
+TEST(ExploitabilityLinuxUtilsTest, TokenizeObjdumpInstructionTest) {
+ ASSERT_FALSE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction("",
+ NULL,
+ NULL,
+ NULL));
+ string line = "0: c7 00 05 00 00 00 mov DWORD PTR [rax],0x5";
+ string operation = "";
+ string dest = "";
+ string src = "";
+ ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
+ &operation,
+ &dest,
+ &src));
+ ASSERT_EQ(operation, "mov");
+ ASSERT_EQ(dest, "[rax]");
+ ASSERT_EQ(src, "0x5");
+ line = "0: c3 ret";
+ ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
+ &operation,
+ &dest,
+ &src));
+ ASSERT_EQ(operation, "ret");
+ ASSERT_EQ(dest, "");
+ ASSERT_EQ(src, "");
+ line = "0: 5f pop rdi";
+ ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
+ &operation,
+ &dest,
+ &src));
+ ASSERT_EQ(operation, "pop");
+ ASSERT_EQ(dest, "rdi");
+ ASSERT_EQ(src, "");
+}
+
+TEST(ExploitabilityLinuxUtilsTest, CalculateAddressTest) {
+ MDRawContextAMD64 raw_context;
+ raw_context.rdx = 12345;
+ ExploitabilityLinuxTestMinidumpContext context(raw_context);
+ ASSERT_EQ(context.GetContextAMD64()->rdx, 12345U);
+ ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("", context, NULL));
+ uint64_t write_address = 0;
+ ASSERT_TRUE(ExploitabilityLinuxTest::CalculateAddress("rdx-0x4D2",
+ context,
+ &write_address));
+ ASSERT_EQ(write_address, 11111U);
+ ASSERT_TRUE(ExploitabilityLinuxTest::CalculateAddress("rdx+0x4D2",
+ context,
+ &write_address));
+ ASSERT_EQ(write_address, 13579U);
+ ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("rdx+rax",
+ context,
+ &write_address));
+ ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("0x3482+0x4D2",
+ context,
+ &write_address));
+}
+#endif // _WIN32
+
+} // namespace
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.cc b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.cc
new file mode 100644
index 000000000..a1f8703a6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.cc
@@ -0,0 +1,283 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// exploitability_win.cc: Windows specific exploitability engine.
+//
+// Provides a guess at the exploitability of the crash for the Windows
+// platform given a minidump and process_state.
+//
+// Author: Cris Neckar
+
+#include <vector>
+
+#include "processor/exploitability_win.h"
+
+#include "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;
+ break;
+ }
+ MinidumpMemoryRegion *instruction_region = 0;
+ if (memory_available) {
+ instruction_region =
+ memory_list->GetMemoryRegionForAddress(instruction_ptr);
+ }
+ if (!near_null && instruction_region &&
+ context->GetContextCPU() == MD_CONTEXT_X86 &&
+ (bad_read || bad_write)) {
+ // Perform checks related to memory around instruction pointer.
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.h b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.h
new file mode 100644
index 000000000..4e08aef03
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/exploitability_win.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// exploitability_win.h: Windows specific exploitability engine.
+//
+// Provides a guess at the exploitability of the crash for the Windows
+// platform given a minidump and process_state.
+//
+// Author: Cris Neckar
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_
+#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/exploitability.h"
+
+namespace google_breakpad {
+
+class ExploitabilityWin : public Exploitability {
+ public:
+ ExploitabilityWin(Minidump *dump,
+ ProcessState *process_state);
+
+ virtual ExploitabilityRating CheckPlatformExploitability();
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver.cc b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver.cc
new file mode 100644
index 000000000..4a3d00071
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver.cc
@@ -0,0 +1,275 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// fast_source_line_resolver.cc: FastSourceLineResolver is a concrete class that
+// implements SourceLineResolverInterface. Both FastSourceLineResolver and
+// BasicSourceLineResolver inherit from SourceLineResolverBase class to reduce
+// code redundancy.
+//
+// See fast_source_line_resolver.h and fast_source_line_resolver_types.h
+// for more documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include "google_breakpad/processor/fast_source_line_resolver.h"
+#include "processor/fast_source_line_resolver_types.h"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+#include "processor/module_factory.h"
+#include "processor/simple_serializer-inl.h"
+
+using std::map;
+using std::make_pair;
+
+namespace google_breakpad {
+
+FastSourceLineResolver::FastSourceLineResolver()
+ : SourceLineResolverBase(new FastModuleFactory) { }
+
+bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() {
+ return false;
+}
+
+void FastSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+
+ // First, look for a FUNC record that covers address. Use
+ // RetrieveNearestRange instead of RetrieveRange so that, if there
+ // is no such function, we can use the next function to bound the
+ // extent of the PUBLIC symbol we find, below. This does mean we
+ // need to check that address indeed falls within the function we
+ // find; do the range comparison in an overflow-friendly way.
+ scoped_ptr<Function> func(new Function);
+ const Function* func_ptr = 0;
+ scoped_ptr<PublicSymbol> public_symbol(new PublicSymbol);
+ const PublicSymbol* public_symbol_ptr = 0;
+ MemAddr function_base;
+ MemAddr function_size;
+ MemAddr public_address;
+
+ if (functions_.RetrieveNearestRange(address, func_ptr,
+ &function_base, &function_size) &&
+ address >= function_base && address - function_base < function_size) {
+ func.get()->CopyFrom(func_ptr);
+ frame->function_name = func->name;
+ frame->function_base = frame->module->base_address() + function_base;
+
+ scoped_ptr<Line> line(new Line);
+ const Line* line_ptr = 0;
+ MemAddr line_base;
+ if (func->lines.RetrieveRange(address, line_ptr, &line_base, NULL)) {
+ line.get()->CopyFrom(line_ptr);
+ FileMap::iterator it = files_.find(line->source_file_id);
+ if (it != files_.end()) {
+ frame->source_file_name =
+ files_.find(line->source_file_id).GetValuePtr();
+ }
+ frame->source_line = line->line;
+ frame->source_line_base = frame->module->base_address() + line_base;
+ }
+ } else if (public_symbols_.Retrieve(address,
+ public_symbol_ptr, &public_address) &&
+ (!func_ptr || public_address > function_base)) {
+ public_symbol.get()->CopyFrom(public_symbol_ptr);
+ frame->function_name = public_symbol->name;
+ frame->function_base = frame->module->base_address() + public_address;
+ }
+}
+
+// WFI: WindowsFrameInfo.
+// Returns a WFI object reading from a raw memory chunk of data
+WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) {
+ 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];
+ }
+
+ // Use pointers to construct Static*Map data members in Module:
+ int map_id = 0;
+ files_ = StaticMap<int, char>(mem_buffer + offsets[map_id++]);
+ functions_ =
+ StaticRangeMap<MemAddr, Function>(mem_buffer + offsets[map_id++]);
+ public_symbols_ =
+ StaticAddressMap<MemAddr, PublicSymbol>(mem_buffer + offsets[map_id++]);
+ for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i)
+ windows_frame_info_[i] =
+ StaticContainedRangeMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
+
+ cfi_initial_rules_ =
+ StaticRangeMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
+ cfi_delta_rules_ = StaticMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
+
+ return true;
+}
+
+WindowsFrameInfo *FastSourceLineResolver::Module::FindWindowsFrameInfo(
+ const StackFrame *frame) const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+ scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
+
+ // We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and
+ // WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order.
+ // WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that
+ // includes its own program string.
+ // WindowsFrameInfo::STACK_INFO_FPO is the older type
+ // corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
+ const char* frame_info_ptr;
+ if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA]
+ .RetrieveRange(address, frame_info_ptr))
+ || (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO]
+ .RetrieveRange(address, frame_info_ptr))) {
+ result->CopyFrom(CopyWFI(frame_info_ptr));
+ return result.release();
+ }
+
+ // Even without a relevant STACK line, many functions contain
+ // information about how much space their parameters consume on the
+ // stack. Use RetrieveNearestRange instead of RetrieveRange, so that
+ // we can use the function to bound the extent of the PUBLIC symbol,
+ // below. However, this does mean we need to check that ADDRESS
+ // falls within the retrieved function's range; do the range
+ // comparison in an overflow-friendly way.
+ scoped_ptr<Function> function(new Function);
+ const Function* function_ptr = 0;
+ MemAddr function_base, function_size;
+ if (functions_.RetrieveNearestRange(address, function_ptr,
+ &function_base, &function_size) &&
+ address >= function_base && address - function_base < function_size) {
+ function.get()->CopyFrom(function_ptr);
+ result->parameter_size = function->parameter_size;
+ result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
+ return result.release();
+ }
+
+ // PUBLIC symbols might have a parameter size. Use the function we
+ // found above to limit the range the public symbol covers.
+ scoped_ptr<PublicSymbol> public_symbol(new PublicSymbol);
+ const PublicSymbol* public_symbol_ptr = 0;
+ MemAddr public_address;
+ if (public_symbols_.Retrieve(address, public_symbol_ptr, &public_address) &&
+ (!function_ptr || public_address > function_base)) {
+ public_symbol.get()->CopyFrom(public_symbol_ptr);
+ result->parameter_size = public_symbol->parameter_size;
+ }
+
+ return NULL;
+}
+
+CFIFrameInfo *FastSourceLineResolver::Module::FindCFIFrameInfo(
+ const StackFrame *frame) const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+ MemAddr initial_base, initial_size;
+ const char* initial_rules = NULL;
+
+ // Find the initial rule whose range covers this address. That
+ // provides an initial set of register recovery rules. Then, walk
+ // forward from the initial rule's starting address to frame's
+ // instruction address, applying delta rules.
+ if (!cfi_initial_rules_.RetrieveRange(address, initial_rules,
+ &initial_base, &initial_size)) {
+ return NULL;
+ }
+
+ // Create a frame info structure, and populate it with the rules from
+ // the STACK CFI INIT record.
+ scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
+ if (!ParseCFIRuleSet(initial_rules, rules.get()))
+ return NULL;
+
+ // Find the first delta rule that falls within the initial rule's range.
+ StaticMap<MemAddr, char>::iterator delta =
+ cfi_delta_rules_.lower_bound(initial_base);
+
+ // Apply delta rules up to and including the frame's address.
+ while (delta != cfi_delta_rules_.end() && delta.GetKey() <= address) {
+ ParseCFIRuleSet(delta.GetValuePtr(), rules.get());
+ delta++;
+ }
+
+ return rules.release();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_types.h b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_types.h
new file mode 100644
index 000000000..2c010470f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_types.h
@@ -0,0 +1,185 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// fast_source_line_resolver_types.h: definition of nested classes/structs in
+// FastSourceLineResolver. It moves the definitions out of
+// fast_source_line_resolver.cc, so that other classes could have access
+// to these private nested types without including fast_source_line_resolver.cc
+//
+// Author: lambxsy@google.com (Siyang Xie)
+
+#ifndef PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
+#define PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
+
+#include "google_breakpad/processor/fast_source_line_resolver.h"
+#include "processor/source_line_resolver_base_types.h"
+
+#include <map>
+#include <string>
+
+#include "google_breakpad/processor/stack_frame.h"
+#include "processor/cfi_frame_info.h"
+#include "processor/static_address_map-inl.h"
+#include "processor/static_contained_range_map-inl.h"
+#include "processor/static_map.h"
+#include "processor/static_range_map-inl.h"
+#include "processor/windows_frame_info.h"
+
+namespace google_breakpad {
+
+struct FastSourceLineResolver::Line : public SourceLineResolverBase::Line {
+ void CopyFrom(const Line *line_ptr) {
+ const char *raw = reinterpret_cast<const char*>(line_ptr);
+ CopyFrom(raw);
+ }
+
+ // De-serialize the memory data of a Line.
+ void CopyFrom(const char *raw) {
+ address = *(reinterpret_cast<const MemAddr*>(raw));
+ size = *(reinterpret_cast<const MemAddr*>(raw + sizeof(address)));
+ source_file_id = *(reinterpret_cast<const int32_t *>(
+ raw + 2 * sizeof(address)));
+ line = *(reinterpret_cast<const int32_t*>(
+ raw + 2 * sizeof(address) + sizeof(source_file_id)));
+ }
+};
+
+struct FastSourceLineResolver::Function :
+public SourceLineResolverBase::Function {
+ void CopyFrom(const Function *func_ptr) {
+ const char *raw = reinterpret_cast<const char*>(func_ptr);
+ CopyFrom(raw);
+ }
+
+ // De-serialize the memory data of a Function.
+ void CopyFrom(const char *raw) {
+ size_t name_size = strlen(raw) + 1;
+ name = raw;
+ address = *(reinterpret_cast<const MemAddr*>(raw + name_size));
+ size = *(reinterpret_cast<const MemAddr*>(
+ raw + name_size + sizeof(MemAddr)));
+ parameter_size = *(reinterpret_cast<const int32_t*>(
+ raw + name_size + 2 * sizeof(MemAddr)));
+ lines = StaticRangeMap<MemAddr, Line>(
+ raw + name_size + 2 * sizeof(MemAddr) + sizeof(int32_t));
+ }
+
+ StaticRangeMap<MemAddr, Line> lines;
+};
+
+struct FastSourceLineResolver::PublicSymbol :
+public SourceLineResolverBase::PublicSymbol {
+ void CopyFrom(const PublicSymbol *public_symbol_ptr) {
+ const char *raw = reinterpret_cast<const char*>(public_symbol_ptr);
+ CopyFrom(raw);
+ }
+
+ // De-serialize the memory data of a PublicSymbol.
+ void CopyFrom(const char *raw) {
+ size_t name_size = strlen(raw) + 1;
+ name = raw;
+ address = *(reinterpret_cast<const MemAddr*>(raw + name_size));
+ parameter_size = *(reinterpret_cast<const int32_t*>(
+ raw + name_size + sizeof(MemAddr)));
+ }
+};
+
+class FastSourceLineResolver::Module: public SourceLineResolverBase::Module {
+ public:
+ explicit Module(const string &name) : name_(name), is_corrupt_(false) { }
+ virtual ~Module() { }
+
+ // Looks up the given relative address, and fills the StackFrame struct
+ // with the result.
+ virtual void LookupAddress(StackFrame *frame) const;
+
+ // Loads a map from the given buffer in char* type.
+ virtual bool LoadMapFromMemory(char *memory_buffer,
+ 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_ = 5 + WindowsFrameInfo::STACK_INFO_LAST;
+
+ private:
+ friend class FastSourceLineResolver;
+ friend class ModuleComparer;
+ typedef StaticMap<int, char> FileMap;
+
+ string name_;
+ StaticMap<int, char> files_;
+ StaticRangeMap<MemAddr, Function> functions_;
+ StaticAddressMap<MemAddr, PublicSymbol> public_symbols_;
+ 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_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_unittest.cc
new file mode 100644
index 000000000..c7215228e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/fast_source_line_resolver_unittest.cc
@@ -0,0 +1,491 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver.
+// Two different approaches for testing fast source line resolver:
+// First, use the same unit test data for basic source line resolver.
+// Second, read data from symbol files, load them as basic modules, and then
+// serialize them and load the serialized data as fast modules. Then compare
+// modules to assure the fast module contains exactly the same data as
+// basic module.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <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::linked_ptr;
+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 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);
+ 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);
+
+ frame.module = &module1;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function1_1");
+ ASSERT_TRUE(frame.module);
+ ASSERT_EQ(frame.module->code_file(), "module1");
+ ASSERT_EQ(frame.function_base, 0x1000U);
+ ASSERT_EQ(frame.source_file_name, "file1_1.cc");
+ ASSERT_EQ(frame.source_line, 44);
+ ASSERT_EQ(frame.source_line_base, 0x1000U);
+ windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_TRUE(windows_frame_info.get());
+ ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
+ ASSERT_EQ(windows_frame_info->program_string,
+ "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
+
+ ClearSourceLineInfo(&frame);
+ frame.instruction = 0x800;
+ frame.module = &module1;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_TRUE(VerifyEmpty(frame));
+ windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_FALSE(windows_frame_info.get());
+
+ frame.instruction = 0x1280;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function1_3");
+ ASSERT_TRUE(frame.source_file_name.empty());
+ ASSERT_EQ(frame.source_line, 0);
+ windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
+ ASSERT_TRUE(windows_frame_info.get());
+ ASSERT_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);
+ 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);
+ ASSERT_EQ(frame.function_name, string("PublicSymbol"));
+
+ frame.instruction = 0x4000;
+ frame.module = &module1;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, string("LargeFunction"));
+
+ frame.instruction = 0x2181;
+ frame.module = &module2;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Function2_2");
+ ASSERT_EQ(frame.function_base, 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);
+ 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);
+ ASSERT_EQ(frame.function_name, "Public2_1");
+
+ ClearSourceLineInfo(&frame);
+ frame.instruction = 0x219f;
+ frame.module = &module2;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_TRUE(frame.function_name.empty());
+
+ frame.instruction = 0x21a0;
+ frame.module = &module2;
+ fast_resolver.FillSourceLineInfo(&frame);
+ ASSERT_EQ(frame.function_name, "Public2_2");
+}
+
+TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
+ TestCodeModule module3("module3");
+ ASSERT_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();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/linked_ptr.h b/toolkit/crashreporter/google-breakpad/src/processor/linked_ptr.h
new file mode 100644
index 000000000..72fbba84a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/linked_ptr.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A "smart" pointer type with reference tracking. Every pointer to a
+// particular object is kept on a circular linked list. When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is assigned, the entire list of pointers to that
+// object is traversed. This class is therefore NOT SUITABLE when there
+// will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+// will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+
+#ifndef PROCESSOR_LINKED_PTR_H__
+#define PROCESSOR_LINKED_PTR_H__
+
+namespace google_breakpad {
+
+// This is used internally by all instances of linked_ptr<>. It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+ // Create a new circle that includes only this instance.
+ void join_new() {
+ next_ = this;
+ }
+
+ // Join an existing circle.
+ void join(linked_ptr_internal const* ptr) {
+ linked_ptr_internal const* p = ptr;
+ while (p->next_ != ptr) p = p->next_;
+ p->next_ = this;
+ next_ = ptr;
+ }
+
+ // Leave whatever circle we're part of. Returns true iff we were the
+ // last member of the circle. Once this is done, you can join() another.
+ bool depart() {
+ if (next_ == this) return true;
+ linked_ptr_internal const* p = next_;
+ while (p->next_ != this) p = p->next_;
+ p->next_ = next_;
+ return false;
+ }
+
+ private:
+ mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+ typedef T element_type;
+
+ // Take over ownership of a raw pointer. This should happen as soon as
+ // possible after the object is created.
+ explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+ ~linked_ptr() { depart(); }
+
+ // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+ template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+ linked_ptr(linked_ptr const& ptr) { copy(&ptr); }
+
+ // Assignment releases the old value and acquires the new.
+ template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+ depart();
+ copy(&ptr);
+ return *this;
+ }
+
+ linked_ptr& operator=(linked_ptr const& ptr) {
+ if (&ptr != this) {
+ depart();
+ copy(&ptr);
+ }
+ return *this;
+ }
+
+ // Smart pointer members.
+ void reset(T* ptr = NULL) { depart(); capture(ptr); }
+ T* get() const { return value_; }
+ T* operator->() const { return value_; }
+ T& operator*() const { return *value_; }
+ // Release ownership of the pointed object and returns it.
+ // Sole ownership by this linked_ptr object is required.
+ T* release() {
+ 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/logging.cc b/toolkit/crashreporter/google-breakpad/src/processor/logging.cc
new file mode 100644
index 000000000..c1eebbc22
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/logging.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// logging.cc: Breakpad logging
+//
+// See logging.h for documentation.
+//
+// Author: Mark Mentovai
+
+#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;
+ }
+
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/logging.h b/toolkit/crashreporter/google-breakpad/src/processor/logging.h
new file mode 100644
index 000000000..406fb67cf
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/logging.h
@@ -0,0 +1,186 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// logging.h: Breakpad logging
+//
+// Breakpad itself uses Breakpad logging with statements of the form:
+// BPLOG(severity) << "message";
+// severity may be INFO, ERROR, or other values defined in this file.
+//
+// BPLOG is an overridable macro so that users can customize Breakpad's
+// logging. Left at the default, logging messages are sent to stderr along
+// with a timestamp and the source code location that produced a message.
+// The streams may be changed by redefining BPLOG_*_STREAM, the logging
+// behavior may be changed by redefining BPLOG_*, and the entire logging
+// system may be overridden by redefining BPLOG(severity). These
+// redefinitions may be passed to the preprocessor as a command-line flag
+// (-D).
+//
+// If an additional header is required to override Breakpad logging, it can
+// be specified by the BP_LOGGING_INCLUDE macro. If defined, this header
+// will #include the header specified by that macro.
+//
+// If any initialization is needed before logging, it can be performed by
+// a function called through the BPLOG_INIT macro. Each main function of
+// an executable program in the Breakpad processor library calls
+// BPLOG_INIT(&argc, &argv); before any logging can be performed; define
+// BPLOG_INIT appropriately if initialization is required.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_LOGGING_H__
+#define PROCESSOR_LOGGING_H__
+
+#include <iostream>
+#include <string>
+
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+#ifdef BP_LOGGING_INCLUDE
+#include BP_LOGGING_INCLUDE
+#endif // BP_LOGGING_INCLUDE
+
+#ifndef THIRD_PARTY_BREAKPAD_GOOGLE_GLUE_LOGGING_H_
+namespace base_logging {
+
+// The open-source copy of logging.h has diverged from Google's internal copy
+// (temporarily, at least). To support the transition to structured logging
+// a definition for base_logging::LogMessage is needed, which is a ostream-
+// like object for streaming arguments to construct a log message.
+typedef std::ostream LogMessage;
+
+} // namespace base_logging
+#endif // THIRD_PARTY_BREAKPAD_GOOGLE_GLUE_LOGGING_H_
+
+namespace google_breakpad {
+
+// These are defined in Microsoft headers.
+#ifdef SEVERITY_ERROR
+#undef SEVERITY_ERROR
+#endif
+
+#ifdef ERROR
+#undef ERROR
+#endif
+
+class LogStream {
+ public:
+ enum Severity {
+ SEVERITY_INFO,
+ SEVERITY_ERROR
+ };
+
+ // Begin logging a message to the stream identified by |stream|, at the
+ // indicated severity. The file and line parameters should be set so as to
+ // identify the line of source code that is producing a message.
+ LogStream(std::ostream &stream, Severity severity,
+ const char *file, int line);
+
+ // Finish logging by printing a newline and flushing the output stream.
+ ~LogStream();
+
+ template<typename T> std::ostream& operator<<(const T &t) {
+ return stream_ << t;
+ }
+
+ private:
+ std::ostream &stream_;
+
+ // Disallow copy constructor and assignment operator
+ explicit LogStream(const LogStream &that);
+ void operator=(const LogStream &that);
+};
+
+// This class is used to explicitly ignore values in the conditional logging
+// macros. This avoids compiler warnings like "value computed is not used"
+// and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() {}
+
+ // This has to be an operator with a precedence lower than << but higher
+ // than ?:
+ void operator&(base_logging::LogMessage &) {}
+};
+
+// 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
+
+#define BPLOG_LAZY_STREAM(stream, condition) \
+ !(condition) ? (void) 0 : \
+ google_breakpad::LogMessageVoidify() & (BPLOG_ ## stream)
+
+#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
+
+#define BPLOG_IF(severity, condition) \
+ BPLOG_LAZY_STREAM(severity, ((condition) && BPLOG_LOG_IS_ON(severity)))
+
+#endif // PROCESSOR_LOGGING_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/map_serializers-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers-inl.h
new file mode 100644
index 000000000..61c7bbd7c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers-inl.h
@@ -0,0 +1,266 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// map_serializers_inl.h: implementation for serializing std::map and its
+// wrapper classes.
+//
+// See map_serializers.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_MAP_SERIALIZERS_INL_H__
+#define PROCESSOR_MAP_SERIALIZERS_INL_H__
+
+#include <map>
+#include <string>
+
+#include "processor/map_serializers.h"
+#include "processor/simple_serializer.h"
+
+#include "processor/address_map-inl.h"
+#include "processor/range_map-inl.h"
+#include "processor/contained_range_map-inl.h"
+
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename Key, typename Value>
+size_t StdMapSerializer<Key, Value>::SizeOf(
+ const std::map<Key, Value> &m) const {
+ size_t size = 0;
+ size_t header_size = (1 + m.size()) * sizeof(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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/map_serializers.h b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers.h
new file mode 100644
index 000000000..a0b9d3fd6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// map_serializers.h: defines templates for serializing std::map and its
+// wrappers: AddressMap, RangeMap, and ContainedRangeMap.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+
+#ifndef PROCESSOR_MAP_SERIALIZERS_H__
+#define PROCESSOR_MAP_SERIALIZERS_H__
+
+#include <map>
+#include <string>
+
+#include "processor/simple_serializer.h"
+
+#include "processor/address_map-inl.h"
+#include "processor/range_map-inl.h"
+#include "processor/contained_range_map-inl.h"
+
+namespace google_breakpad {
+
+// StdMapSerializer allocates memory and serializes an std::map instance into a
+// chunk of memory data.
+template<typename Key, typename Value>
+class StdMapSerializer {
+ public:
+ // Calculate the memory size of serialized data.
+ size_t SizeOf(const std::map<Key, Value> &m) const;
+
+ // Writes the serialized data to memory with start address = dest,
+ // and returns the "end" of data, i.e., return the address follow the final
+ // byte of data.
+ // NOTE: caller has to allocate enough memory before invoke Write() method.
+ char* Write(const std::map<Key, Value> &m, char* dest) const;
+
+ // Serializes a std::map object into a chunk of memory data with format
+ // described in "StaticMap.h" comment.
+ // Returns a pointer to the serialized data. If size != NULL, *size is set
+ // to the size of serialized data, i.e., SizeOf(m).
+ // Caller has the ownership of memory allocated as "new char[]".
+ char* Serialize(const std::map<Key, Value> &m, unsigned int *size) const;
+
+ private:
+ SimpleSerializer<Key> key_serializer_;
+ SimpleSerializer<Value> value_serializer_;
+};
+
+// AddressMapSerializer allocates memory and serializes an AddressMap into a
+// chunk of memory data.
+template<typename Addr, typename Entry>
+class AddressMapSerializer {
+ public:
+ // Calculate the memory size of serialized data.
+ size_t SizeOf(const AddressMap<Addr, Entry> &m) const {
+ return std_map_serializer_.SizeOf(m.map_);
+ }
+
+ // Write the serialized data to specified memory location. Return the "end"
+ // of data, i.e., return the address after the final byte of data.
+ // NOTE: caller has to allocate enough memory before invoke Write() method.
+ char* Write(const AddressMap<Addr, Entry> &m, char *dest) const {
+ return std_map_serializer_.Write(m.map_, dest);
+ }
+
+ // Serializes an AddressMap object into a chunk of memory data.
+ // Returns a pointer to the serialized data. If size != NULL, *size is set
+ // to the size of serialized data, i.e., SizeOf(m).
+ // Caller has the ownership of memory allocated as "new char[]".
+ char* Serialize(const AddressMap<Addr, Entry> &m, unsigned int *size) const {
+ return std_map_serializer_.Serialize(m.map_, size);
+ }
+
+ private:
+ // AddressMapSerializer is a simple wrapper of StdMapSerializer, just as
+ // AddressMap is a simple wrapper of std::map.
+ StdMapSerializer<Addr, Entry> std_map_serializer_;
+};
+
+// RangeMapSerializer allocates memory and serializes a RangeMap instance into a
+// chunk of memory data.
+template<typename Address, typename Entry>
+class RangeMapSerializer {
+ public:
+ // Calculate the memory size of serialized data.
+ size_t SizeOf(const RangeMap<Address, Entry> &m) const;
+
+ // Write the serialized data to specified memory location. Return the "end"
+ // of data, i.e., return the address after the final byte of data.
+ // NOTE: caller has to allocate enough memory before invoke Write() method.
+ char* Write(const RangeMap<Address, Entry> &m, char* dest) const;
+
+ // Serializes a RangeMap object into a chunk of memory data.
+ // Returns a pointer to the serialized data. If size != NULL, *size is set
+ // to the size of serialized data, i.e., SizeOf(m).
+ // Caller has the ownership of memory allocated as "new char[]".
+ char* Serialize(const RangeMap<Address, Entry> &m, unsigned int *size) const;
+
+ private:
+ // Convenient type name for Range.
+ typedef typename RangeMap<Address, Entry>::Range Range;
+
+ // Serializer for RangeMap's key and Range::base_.
+ SimpleSerializer<Address> address_serializer_;
+ // Serializer for RangeMap::Range::entry_.
+ SimpleSerializer<Entry> entry_serializer_;
+};
+
+// ContainedRangeMapSerializer allocates memory and serializes a
+// ContainedRangeMap instance into a chunk of memory data.
+template<class AddrType, class EntryType>
+class ContainedRangeMapSerializer {
+ public:
+ // Calculate the memory size of serialized data.
+ size_t SizeOf(const ContainedRangeMap<AddrType, EntryType> *m) const;
+
+ // Write the serialized data to specified memory location. Return the "end"
+ // of data, i.e., return the address after the final byte of data.
+ // NOTE: caller has to allocate enough memory before invoke Write() method.
+ char* Write(const ContainedRangeMap<AddrType, EntryType> *m,
+ char* dest) const;
+
+ // Serializes a ContainedRangeMap object into a chunk of memory data.
+ // Returns a pointer to the serialized data. If size != NULL, *size is set
+ // to the size of serialized data, i.e., SizeOf(m).
+ // Caller has the ownership of memory allocated as "new char[]".
+ char* Serialize(const ContainedRangeMap<AddrType, EntryType> *m,
+ unsigned int *size) const;
+
+ private:
+ // Convenient type name for the underlying map type.
+ typedef std::map<AddrType, ContainedRangeMap<AddrType, EntryType>*> Map;
+
+ // Serializer for addresses and entries stored in ContainedRangeMap.
+ SimpleSerializer<AddrType> addr_serializer_;
+ SimpleSerializer<EntryType> entry_serializer_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_MAP_SERIALIZERS_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/map_serializers_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers_unittest.cc
new file mode 100644
index 000000000..0d872ec2e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/map_serializers_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// map_serializers_unittest.cc: Unit tests for std::map serializer and
+// std::map wrapper serializers.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <climits>
+#include <map>
+#include <string>
+#include <utility>
+#include <iostream>
+#include <sstream>
+
+#include "breakpad_googletest_includes.h"
+#include "map_serializers-inl.h"
+
+#include "processor/address_map-inl.h"
+#include "processor/range_map-inl.h"
+#include "processor/contained_range_map-inl.h"
+
+typedef int32_t AddrType;
+typedef int32_t EntryType;
+
+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();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc b/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc
new file mode 100644
index 000000000..4af62f56f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc
@@ -0,0 +1,385 @@
+// Copyright (c) 2014 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// microdump.cc: A microdump reader.
+//
+// See microdump.h for documentation.
+
+#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/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 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_.SetEnableShrinkDown(is_enabled);
+}
+
+//
+// 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()) {
+ 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)) {
+ std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
+ << " bytes instead of " << sizeof(MDRawContextARM64)
+ << std::endl;
+ continue;
+ }
+ MDRawContextARM64* arm = new MDRawContextARM64();
+ memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
+ context_->SetContextARM64(arm);
+ } 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(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
+
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor.cc b/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor.cc
new file mode 100644
index 000000000..366e3f30a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// microdump_processor.cc: A microdump processor.
+//
+// See microdump_processor.h for documentation.
+
+#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(const string &microdump_contents,
+ ProcessState* process_state) {
+ assert(process_state);
+
+ process_state->Clear();
+
+ if (microdump_contents.empty()) {
+ BPLOG(ERROR) << "Microdump is empty.";
+ return PROCESS_ERROR_MINIDUMP_NOT_FOUND;
+ }
+
+ Microdump microdump(microdump_contents);
+ process_state->modules_ = microdump.GetModules()->Copy();
+ scoped_ptr<Stackwalker> stackwalker(
+ Stackwalker::StackwalkerForCPU(
+ &process_state->system_info_,
+ microdump.GetContext(),
+ microdump.GetMemory(),
+ process_state->modules_,
+ 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();
+
+ return PROCESS_OK;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor_unittest.cc
new file mode 100644
index 000000000..af897f7da
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_processor_unittest.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Unit test for MicrodumpProcessor.
+
+#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_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::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);
+
+ return processor.Process(microdump_contents, 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, &microdump_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_Empty) {
+ ProcessState state;
+ google_breakpad::ProcessResult result =
+ ProcessMicrodump("", "", &state);
+ ASSERT_EQ(google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND, result);
+}
+
+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_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(12U, 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(56U, 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();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk.cc b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk.cc
new file mode 100644
index 000000000..7ea80495a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2014 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// microdump_stackwalk.cc: Process a microdump with MicrodumpProcessor, printing
+// the results, including stack traces.
+
+#include <stdio.h>
+#include <string.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#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_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 {
+
+using google_breakpad::BasicSourceLineResolver;
+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 |microdump_file| using MicrodumpProcessor. |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 char* microdump_file,
+ const std::vector<string>& symbol_paths,
+ bool machine_readable) {
+ std::ifstream file_stream(microdump_file);
+ std::vector<char> bytes;
+ file_stream.seekg(0, std::ios_base::end);
+ bytes.resize(file_stream.tellg());
+ 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 (!symbol_paths.empty()) {
+ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
+ }
+
+ BasicSourceLineResolver resolver;
+ StackFrameSymbolizer frame_symbolizer(symbol_supplier.get(), &resolver);
+ ProcessState process_state;
+ MicrodumpProcessor microdump_processor(&frame_symbolizer);
+ ProcessResult res = microdump_processor.Process(microdump_content,
+ &process_state);
+
+ if (res == google_breakpad::PROCESS_OK) {
+ if (machine_readable) {
+ PrintProcessStateMachineReadable(process_state);
+ } else {
+ PrintProcessState(process_state, false, &resolver);
+ }
+ return 0;
+ }
+
+ BPLOG(ERROR) << "MicrodumpProcessor::Process failed (code = " << res << ")";
+ return 1;
+}
+
+void usage(const char *program_name) {
+ fprintf(stderr, "usage: %s [-m] <microdump-file> [symbol-path ...]\n"
+ " -m : Output in machine-readable format\n",
+ program_name);
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ const char* microdump_file;
+ bool machine_readable;
+ int symbol_path_arg;
+
+ if (strcmp(argv[1], "-m") == 0) {
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ machine_readable = true;
+ microdump_file = argv[2];
+ symbol_path_arg = 3;
+ } else {
+ machine_readable = false;
+ microdump_file = argv[1];
+ symbol_path_arg = 2;
+ }
+
+ // extra arguments are symbol paths
+ std::vector<string> symbol_paths;
+ if (argc > symbol_path_arg) {
+ for (int argi = symbol_path_arg; argi < argc; ++argi)
+ symbol_paths.push_back(argv[argi]);
+ }
+
+ return PrintMicrodumpProcess(microdump_file,
+ symbol_paths,
+ machine_readable);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_machine_readable_test b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_machine_readable_test
new file mode 100755
index 000000000..fadec2645
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_machine_readable_test
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Copyright (c) 2014, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+source "${0%/*}/microdump_stackwalk_test_vars" # 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test
new file mode 100755
index 000000000..5a1f3d59f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Copyright (c) 2014, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+source "${0%/*}/microdump_stackwalk_test_vars" # 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test_vars b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test_vars
new file mode 100644
index 000000000..a8b0e0df5
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump_stackwalk_test_vars
@@ -0,0 +1 @@
+MICRODUMP_SUPPORTED_ARCHS="arm arm64"
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc
new file mode 100644
index 000000000..1e1d386df
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc
@@ -0,0 +1,4989 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// minidump.cc: A minidump reader.
+//
+// See minidump.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "google_breakpad/processor/minidump.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else // _WIN32
+#include <unistd.h>
+#endif // _WIN32
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <vector>
+
+#include "processor/range_map-inl.h"
+
+#include "common/scoped_ptr.h"
+#include "common/stdio_wrapper.h"
+#include "google_breakpad/processor/dump_context.h"
+#include "processor/basic_code_module.h"
+#include "processor/basic_code_modules.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+
+using std::istream;
+using std::ifstream;
+using std::numeric_limits;
+using std::vector;
+
+// Returns true iff |context_size| matches exactly one of the sizes of the
+// various MDRawContext* types.
+// TODO(blundell): This function can be removed once
+// http://code.google.com/p/google-breakpad/issues/detail?id=550 is fixed.
+static bool IsContextSizeUnique(uint32_t context_size) {
+ int num_matching_contexts = 0;
+ if (context_size == sizeof(MDRawContextX86))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextPPC))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextPPC64))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextAMD64))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextSPARC))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextARM))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextARM64))
+ num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextMIPS))
+ num_matching_contexts++;
+ return num_matching_contexts == 1;
+}
+
+//
+// Swapping routines
+//
+// Inlining these doesn't increase code size significantly, and it saves
+// a whole lot of unnecessary jumping back and forth.
+//
+
+
+// Swapping an 8-bit quantity is a no-op. This function is only provided
+// to account for certain templatized operations that require swapping for
+// wider types but handle uint8_t too
+// (MinidumpMemoryRegion::GetMemoryAtAddressInternal).
+static inline void Swap(uint8_t* value) {
+}
+
+
+// Optimization: don't need to AND the furthest right shift, because we're
+// shifting an unsigned quantity. The standard requires zero-filling in this
+// case. If the quantities were signed, a bitmask whould be needed for this
+// right shift to avoid an arithmetic shift (which retains the sign bit).
+// The furthest left shift never needs to be ANDed bitmask.
+
+
+static inline void Swap(uint16_t* value) {
+ *value = (*value >> 8) |
+ (*value << 8);
+}
+
+
+static inline void Swap(uint32_t* value) {
+ *value = (*value >> 24) |
+ ((*value >> 8) & 0x0000ff00) |
+ ((*value << 8) & 0x00ff0000) |
+ (*value << 24);
+}
+
+
+static inline void Swap(uint64_t* value) {
+ uint32_t* value32 = reinterpret_cast<uint32_t*>(value);
+ Swap(&value32[0]);
+ Swap(&value32[1]);
+ uint32_t temp = value32[0];
+ value32[0] = value32[1];
+ value32[1] = temp;
+}
+
+
+// Given a pointer to a 128-bit int in the minidump data, set the "low"
+// and "high" fields appropriately.
+static void Normalize128(uint128_struct* value, bool is_big_endian) {
+ // The struct format is [high, low], so if the format is big-endian,
+ // the most significant bytes will already be in the high field.
+ if (!is_big_endian) {
+ uint64_t temp = value->low;
+ value->low = value->high;
+ value->high = temp;
+ }
+}
+
+// This just swaps each int64 half of the 128-bit value.
+// The value should also be normalized by calling Normalize128().
+static void Swap(uint128_struct* value) {
+ Swap(&value->low);
+ Swap(&value->high);
+}
+
+// Swapping signed integers
+static inline void Swap(int32_t* value) {
+ Swap(reinterpret_cast<uint32_t*>(value));
+}
+
+static inline void Swap(MDLocationDescriptor* location_descriptor) {
+ Swap(&location_descriptor->data_size);
+ Swap(&location_descriptor->rva);
+}
+
+
+static inline void Swap(MDMemoryDescriptor* memory_descriptor) {
+ Swap(&memory_descriptor->start_of_memory_range);
+ Swap(&memory_descriptor->memory);
+}
+
+
+static inline void Swap(MDGUID* guid) {
+ Swap(&guid->data1);
+ Swap(&guid->data2);
+ Swap(&guid->data3);
+ // Don't swap guid->data4[] because it contains 8-bit quantities.
+}
+
+static inline void Swap(MDSystemTime* system_time) {
+ Swap(&system_time->year);
+ Swap(&system_time->month);
+ Swap(&system_time->day_of_week);
+ Swap(&system_time->day);
+ Swap(&system_time->hour);
+ Swap(&system_time->minute);
+ Swap(&system_time->second);
+ Swap(&system_time->milliseconds);
+}
+
+static inline void Swap(MDXStateFeature* xstate_feature) {
+ Swap(&xstate_feature->offset);
+ Swap(&xstate_feature->size);
+}
+
+static inline void Swap(MDXStateConfigFeatureMscInfo* xstate_feature_info) {
+ Swap(&xstate_feature_info->size_of_info);
+ Swap(&xstate_feature_info->context_size);
+ Swap(&xstate_feature_info->enabled_features);
+
+ for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
+ Swap(&xstate_feature_info->features[i]);
+ }
+}
+
+static inline void Swap(uint16_t* data, size_t size_in_bytes) {
+ size_t data_length = size_in_bytes / sizeof(data[0]);
+ for (size_t i = 0; i < data_length; i++) {
+ Swap(&data[i]);
+ }
+}
+
+//
+// Character conversion routines
+//
+
+
+// Standard wide-character conversion routines depend on the system's own
+// idea of what width a wide character should be: some use 16 bits, and
+// some use 32 bits. For the purposes of a minidump, wide strings are
+// always represented with 16-bit UTF-16 chracters. iconv isn't available
+// everywhere, and its interface varies where it is available. iconv also
+// deals purely with char* pointers, so in addition to considering the swap
+// parameter, a converter that uses iconv would also need to take the host
+// CPU's endianness into consideration. It doesn't seems worth the trouble
+// of making it a dependency when we don't care about anything but UTF-16.
+static string* UTF16ToUTF8(const vector<uint16_t>& in,
+ bool swap) {
+ scoped_ptr<string> out(new string());
+
+ // Set the string's initial capacity to the number of UTF-16 characters,
+ // because the UTF-8 representation will always be at least this long.
+ // If the UTF-8 representation is longer, the string will grow dynamically.
+ out->reserve(in.size());
+
+ for (vector<uint16_t>::const_iterator iterator = in.begin();
+ iterator != in.end();
+ ++iterator) {
+ // Get a 16-bit value from the input
+ uint16_t in_word = *iterator;
+ if (swap)
+ Swap(&in_word);
+
+ // Convert the input value (in_word) into a Unicode code point (unichar).
+ uint32_t unichar;
+ if (in_word >= 0xdc00 && in_word <= 0xdcff) {
+ BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " <<
+ HexString(in_word) << " without high";
+ return NULL;
+ } else if (in_word >= 0xd800 && in_word <= 0xdbff) {
+ // High surrogate.
+ unichar = (in_word - 0xd7c0) << 10;
+ if (++iterator == in.end()) {
+ BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
+ HexString(in_word) << " at end of string";
+ return NULL;
+ }
+ uint32_t high_word = in_word;
+ in_word = *iterator;
+ if (in_word < 0xdc00 || in_word > 0xdcff) {
+ BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
+ HexString(high_word) << " without low " <<
+ HexString(in_word);
+ return NULL;
+ }
+ unichar |= in_word & 0x03ff;
+ } else {
+ // The ordinary case, a single non-surrogate Unicode character encoded
+ // as a single 16-bit value.
+ unichar = in_word;
+ }
+
+ // Convert the Unicode code point (unichar) into its UTF-8 representation,
+ // appending it to the out string.
+ if (unichar < 0x80) {
+ (*out) += static_cast<char>(unichar);
+ } else if (unichar < 0x800) {
+ (*out) += 0xc0 | static_cast<char>(unichar >> 6);
+ (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
+ } else if (unichar < 0x10000) {
+ (*out) += 0xe0 | static_cast<char>(unichar >> 12);
+ (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
+ (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
+ } else if (unichar < 0x200000) {
+ (*out) += 0xf0 | static_cast<char>(unichar >> 18);
+ (*out) += 0x80 | static_cast<char>((unichar >> 12) & 0x3f);
+ (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
+ (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
+ } else {
+ BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " <<
+ HexString(unichar) << " in UTF-8";
+ return NULL;
+ }
+ }
+
+ return out.release();
+}
+
+// Return the smaller of the number of code units in the UTF-16 string,
+// not including the terminating null word, or maxlen.
+static size_t UTF16codeunits(const uint16_t *string, size_t maxlen) {
+ size_t count = 0;
+ while (count < maxlen && string[count] != 0)
+ count++;
+ return count;
+}
+
+static inline void Swap(MDTimeZoneInformation* time_zone) {
+ Swap(&time_zone->bias);
+ // Skip time_zone->standard_name. No need to swap UTF-16 fields.
+ // The swap will be done as part of the conversion to UTF-8.
+ Swap(&time_zone->standard_date);
+ Swap(&time_zone->standard_bias);
+ // Skip time_zone->daylight_name. No need to swap UTF-16 fields.
+ // The swap will be done as part of the conversion to UTF-8.
+ Swap(&time_zone->daylight_date);
+ Swap(&time_zone->daylight_bias);
+}
+
+static void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data,
+ size_t max_length_in_bytes,
+ string* utf8_result,
+ bool swap) {
+ // Since there is no explicit byte length for each string, use
+ // UTF16codeunits to calculate word length, then derive byte
+ // length from that.
+ size_t max_word_length = max_length_in_bytes / sizeof(utf16_data[0]);
+ size_t word_length = UTF16codeunits(utf16_data, max_word_length);
+ if (word_length > 0) {
+ size_t byte_length = word_length * sizeof(utf16_data[0]);
+ vector<uint16_t> utf16_vector(word_length);
+ memcpy(&utf16_vector[0], &utf16_data[0], byte_length);
+ scoped_ptr<string> temp(UTF16ToUTF8(utf16_vector, swap));
+ if (temp.get()) {
+ utf8_result->assign(*temp);
+ }
+ } else {
+ utf8_result->clear();
+ }
+}
+
+
+// For fields that may or may not be valid, PrintValueOrInvalid will print the
+// string "(invalid)" if the field is not valid, and will print the value if
+// the field is valid. The value is printed as hexadecimal or decimal.
+
+enum NumberFormat {
+ kNumberFormatDecimal,
+ kNumberFormatHexadecimal,
+};
+
+static void PrintValueOrInvalid(bool valid,
+ NumberFormat number_format,
+ uint32_t value) {
+ if (!valid) {
+ printf("(invalid)\n");
+ } else if (number_format == kNumberFormatDecimal) {
+ printf("%d\n", value);
+ } else {
+ printf("0x%x\n", value);
+ }
+}
+
+// Converts a time_t to a string showing the time in UTC.
+string TimeTToUTCString(time_t tt) {
+ struct tm timestruct;
+#ifdef _WIN32
+ gmtime_s(&timestruct, &tt);
+#else
+ gmtime_r(&tt, &timestruct);
+#endif
+
+ char timestr[20];
+ int rv = strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", &timestruct);
+ if (rv == 0) {
+ return string();
+ }
+
+ return string(timestr);
+}
+
+
+//
+// MinidumpObject
+//
+
+
+MinidumpObject::MinidumpObject(Minidump* minidump)
+ : DumpObject(),
+ minidump_(minidump) {
+}
+
+
+//
+// MinidumpStream
+//
+
+
+MinidumpStream::MinidumpStream(Minidump* minidump)
+ : MinidumpObject(minidump) {
+}
+
+
+//
+// MinidumpContext
+//
+
+
+MinidumpContext::MinidumpContext(Minidump* minidump)
+ : DumpContext(),
+ minidump_(minidump) {
+}
+
+MinidumpContext::~MinidumpContext() {
+}
+
+bool MinidumpContext::Read(uint32_t expected_size) {
+ valid_ = false;
+
+ // Certain raw context types are currently assumed to have unique sizes.
+ if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
+ BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
+ << "other raw context";
+ return false;
+ }
+ if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
+ BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
+ << "other raw context";
+ return false;
+ }
+ if (!IsContextSizeUnique(sizeof(MDRawContextARM64))) {
+ BPLOG(ERROR) << "sizeof(MDRawContextARM64) cannot match the size of any "
+ << "other raw context";
+ return false;
+ }
+
+ FreeContext();
+
+ // First, figure out what type of CPU this context structure is for.
+ // For some reason, the AMD64 Context doesn't have context_flags
+ // at the beginning of the structure, so special case it here.
+ if (expected_size == sizeof(MDRawContextAMD64)) {
+ BPLOG(INFO) << "MinidumpContext: looks like AMD64 context";
+
+ scoped_ptr<MDRawContextAMD64> context_amd64(new MDRawContextAMD64());
+ if (!minidump_->ReadBytes(context_amd64.get(),
+ sizeof(MDRawContextAMD64))) {
+ BPLOG(ERROR) << "MinidumpContext could not read amd64 context";
+ return false;
+ }
+
+ if (minidump_->swap())
+ Swap(&context_amd64->context_flags);
+
+ uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK;
+ if (cpu_type == 0) {
+ if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
+ context_amd64->context_flags |= cpu_type;
+ } else {
+ BPLOG(ERROR) << "Failed to preserve the current stream position";
+ return false;
+ }
+ }
+
+ if (cpu_type != MD_CONTEXT_AMD64) {
+ // TODO: Fall through to switch below.
+ // http://code.google.com/p/google-breakpad/issues/detail?id=550
+ BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext amd64 does not match system info";
+ return false;
+ }
+
+ // Normalize the 128-bit types in the dump.
+ // Since this is AMD64, by definition, the values are little-endian.
+ for (unsigned int vr_index = 0;
+ vr_index < MD_CONTEXT_AMD64_VR_COUNT;
+ ++vr_index)
+ Normalize128(&context_amd64->vector_register[vr_index], false);
+
+ if (minidump_->swap()) {
+ Swap(&context_amd64->p1_home);
+ Swap(&context_amd64->p2_home);
+ Swap(&context_amd64->p3_home);
+ Swap(&context_amd64->p4_home);
+ Swap(&context_amd64->p5_home);
+ Swap(&context_amd64->p6_home);
+ // context_flags is already swapped
+ Swap(&context_amd64->mx_csr);
+ Swap(&context_amd64->cs);
+ Swap(&context_amd64->ds);
+ Swap(&context_amd64->es);
+ Swap(&context_amd64->fs);
+ Swap(&context_amd64->ss);
+ Swap(&context_amd64->eflags);
+ Swap(&context_amd64->dr0);
+ Swap(&context_amd64->dr1);
+ Swap(&context_amd64->dr2);
+ Swap(&context_amd64->dr3);
+ Swap(&context_amd64->dr6);
+ Swap(&context_amd64->dr7);
+ Swap(&context_amd64->rax);
+ Swap(&context_amd64->rcx);
+ Swap(&context_amd64->rdx);
+ Swap(&context_amd64->rbx);
+ Swap(&context_amd64->rsp);
+ Swap(&context_amd64->rbp);
+ Swap(&context_amd64->rsi);
+ Swap(&context_amd64->rdi);
+ Swap(&context_amd64->r8);
+ Swap(&context_amd64->r9);
+ Swap(&context_amd64->r10);
+ Swap(&context_amd64->r11);
+ Swap(&context_amd64->r12);
+ Swap(&context_amd64->r13);
+ Swap(&context_amd64->r14);
+ Swap(&context_amd64->r15);
+ Swap(&context_amd64->rip);
+ // FIXME: I'm not sure what actually determines
+ // which member of the union {flt_save, sse_registers}
+ // is valid. We're not currently using either,
+ // but it would be good to have them swapped properly.
+
+ for (unsigned int vr_index = 0;
+ vr_index < MD_CONTEXT_AMD64_VR_COUNT;
+ ++vr_index)
+ Swap(&context_amd64->vector_register[vr_index]);
+ Swap(&context_amd64->vector_control);
+ Swap(&context_amd64->debug_control);
+ Swap(&context_amd64->last_branch_to_rip);
+ Swap(&context_amd64->last_branch_from_rip);
+ Swap(&context_amd64->last_exception_to_rip);
+ Swap(&context_amd64->last_exception_from_rip);
+ }
+
+ SetContextFlags(context_amd64->context_flags);
+
+ SetContextAMD64(context_amd64.release());
+ } else if (expected_size == sizeof(MDRawContextPPC64)) {
+ // |context_flags| of MDRawContextPPC64 is 64 bits, but other MDRawContext
+ // in the else case have 32 bits |context_flags|, so special case it here.
+ uint64_t context_flags;
+ if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
+ BPLOG(ERROR) << "MinidumpContext could not read context flags";
+ return false;
+ }
+ if (minidump_->swap())
+ Swap(&context_flags);
+
+ uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
+ scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
+
+ if (cpu_type == 0) {
+ if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
+ context_ppc64->context_flags |= cpu_type;
+ } else {
+ BPLOG(ERROR) << "Failed to preserve the current stream position";
+ return false;
+ }
+ }
+
+ if (cpu_type != MD_CONTEXT_PPC64) {
+ // TODO: Fall through to switch below.
+ // http://code.google.com/p/google-breakpad/issues/detail?id=550
+ BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
+ return false;
+ }
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_ppc64->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_ppc64->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_ppc64.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextPPC64) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read ppc64 context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext ppc64 does not match system info";
+ return false;
+ }
+ if (minidump_->swap()) {
+ // context_ppc64->context_flags was already swapped.
+ Swap(&context_ppc64->srr0);
+ Swap(&context_ppc64->srr1);
+ for (unsigned int gpr_index = 0;
+ gpr_index < MD_CONTEXT_PPC64_GPR_COUNT;
+ ++gpr_index) {
+ Swap(&context_ppc64->gpr[gpr_index]);
+ }
+ Swap(&context_ppc64->cr);
+ Swap(&context_ppc64->xer);
+ Swap(&context_ppc64->lr);
+ Swap(&context_ppc64->ctr);
+ Swap(&context_ppc64->vrsave);
+ for (unsigned int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_ppc64->float_save.fpregs[fpr_index]);
+ }
+ // Don't swap context_ppc64->float_save.fpscr_pad because it is only
+ // used for padding.
+ Swap(&context_ppc64->float_save.fpscr);
+ for (unsigned int vr_index = 0;
+ vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
+ ++vr_index) {
+ Normalize128(&context_ppc64->vector_save.save_vr[vr_index], true);
+ Swap(&context_ppc64->vector_save.save_vr[vr_index]);
+ }
+ Swap(&context_ppc64->vector_save.save_vscr);
+ // Don't swap the padding fields in vector_save.
+ Swap(&context_ppc64->vector_save.save_vrvalid);
+ }
+
+ SetContextFlags(static_cast<uint32_t>(context_ppc64->context_flags));
+
+ // Check for data loss when converting context flags from uint64_t into
+ // uint32_t
+ if (static_cast<uint64_t>(GetContextFlags()) !=
+ context_ppc64->context_flags) {
+ BPLOG(ERROR) << "Data loss detected when converting PPC64 context_flags";
+ return false;
+ }
+
+ SetContextPPC64(context_ppc64.release());
+ } else if (expected_size == sizeof(MDRawContextARM64)) {
+ // |context_flags| of MDRawContextARM64 is 64 bits, but other MDRawContext
+ // in the else case have 32 bits |context_flags|, so special case it here.
+ uint64_t context_flags;
+
+ BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
+
+ if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
+ BPLOG(ERROR) << "MinidumpContext could not read context flags";
+ return false;
+ }
+ if (minidump_->swap())
+ Swap(&context_flags);
+
+ scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
+
+ uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
+ if (cpu_type == 0) {
+ if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
+ context_arm64->context_flags |= cpu_type;
+ } else {
+ BPLOG(ERROR) << "Failed to preserve the current stream position";
+ return false;
+ }
+ }
+
+ if (cpu_type != MD_CONTEXT_ARM64) {
+ // TODO: Fall through to switch below.
+ // http://code.google.com/p/google-breakpad/issues/detail?id=550
+ BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
+ return false;
+ }
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_arm64->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_arm64->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextARM64) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_arm64->context_flags was already swapped.
+ for (unsigned int ireg_index = 0;
+ ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
+ ++ireg_index) {
+ Swap(&context_arm64->iregs[ireg_index]);
+ }
+ Swap(&context_arm64->cpsr);
+ Swap(&context_arm64->float_save.fpsr);
+ Swap(&context_arm64->float_save.fpcr);
+ for (unsigned int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
+ ++fpr_index) {
+ // While ARM64 is bi-endian, iOS (currently the only platform
+ // for which ARM64 support has been brought up) uses ARM64 exclusively
+ // in little-endian mode.
+ Normalize128(&context_arm64->float_save.regs[fpr_index], false);
+ Swap(&context_arm64->float_save.regs[fpr_index]);
+ }
+ }
+ SetContextFlags(static_cast<uint32_t>(context_arm64->context_flags));
+
+ // Check for data loss when converting context flags from uint64_t into
+ // uint32_t
+ if (static_cast<uint64_t>(GetContextFlags()) !=
+ context_arm64->context_flags) {
+ BPLOG(ERROR) << "Data loss detected when converting ARM64 context_flags";
+ return false;
+ }
+
+ SetContextARM64(context_arm64.release());
+ } else {
+ uint32_t context_flags;
+ if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
+ BPLOG(ERROR) << "MinidumpContext could not read context flags";
+ return false;
+ }
+ if (minidump_->swap())
+ Swap(&context_flags);
+
+ uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
+ if (cpu_type == 0) {
+ // Unfortunately the flag for MD_CONTEXT_ARM that was taken
+ // from a Windows CE SDK header conflicts in practice with
+ // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered,
+ // but handle dumps with the legacy value gracefully here.
+ if (context_flags & MD_CONTEXT_ARM_OLD) {
+ context_flags |= MD_CONTEXT_ARM;
+ context_flags &= ~MD_CONTEXT_ARM_OLD;
+ cpu_type = MD_CONTEXT_ARM;
+ }
+ }
+
+ if (cpu_type == 0) {
+ if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
+ context_flags |= cpu_type;
+ } else {
+ BPLOG(ERROR) << "Failed to preserve the current stream position";
+ return false;
+ }
+ }
+
+ // Allocate the context structure for the correct CPU and fill it. The
+ // casts are slightly unorthodox, but it seems better to do that than to
+ // maintain a separate pointer for each type of CPU context structure
+ // when only one of them will be used.
+ switch (cpu_type) {
+ case MD_CONTEXT_X86: {
+ if (expected_size != sizeof(MDRawContextX86)) {
+ BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " <<
+ expected_size << " != " << sizeof(MDRawContextX86);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextX86> context_x86(new MDRawContextX86());
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_x86->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_x86->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_x86.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextX86) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read x86 context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext x86 does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_x86->context_flags was already swapped.
+ Swap(&context_x86->dr0);
+ Swap(&context_x86->dr1);
+ Swap(&context_x86->dr2);
+ Swap(&context_x86->dr3);
+ Swap(&context_x86->dr6);
+ Swap(&context_x86->dr7);
+ Swap(&context_x86->float_save.control_word);
+ Swap(&context_x86->float_save.status_word);
+ Swap(&context_x86->float_save.tag_word);
+ Swap(&context_x86->float_save.error_offset);
+ Swap(&context_x86->float_save.error_selector);
+ Swap(&context_x86->float_save.data_offset);
+ Swap(&context_x86->float_save.data_selector);
+ // context_x86->float_save.register_area[] contains 8-bit quantities
+ // and does not need to be swapped.
+ Swap(&context_x86->float_save.cr0_npx_state);
+ Swap(&context_x86->gs);
+ Swap(&context_x86->fs);
+ Swap(&context_x86->es);
+ Swap(&context_x86->ds);
+ Swap(&context_x86->edi);
+ Swap(&context_x86->esi);
+ Swap(&context_x86->ebx);
+ Swap(&context_x86->edx);
+ Swap(&context_x86->ecx);
+ Swap(&context_x86->eax);
+ Swap(&context_x86->ebp);
+ Swap(&context_x86->eip);
+ Swap(&context_x86->cs);
+ Swap(&context_x86->eflags);
+ Swap(&context_x86->esp);
+ Swap(&context_x86->ss);
+ // context_x86->extended_registers[] contains 8-bit quantities and
+ // does not need to be swapped.
+ }
+
+ SetContextX86(context_x86.release());
+
+ break;
+ }
+
+ case MD_CONTEXT_PPC: {
+ if (expected_size != sizeof(MDRawContextPPC)) {
+ BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " <<
+ expected_size << " != " << sizeof(MDRawContextPPC);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextPPC> context_ppc(new MDRawContextPPC());
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_ppc->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_ppc->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_ppc.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextPPC) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read ppc context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext ppc does not match system info";
+ return false;
+ }
+
+ // Normalize the 128-bit types in the dump.
+ // Since this is PowerPC, by definition, the values are big-endian.
+ for (unsigned int vr_index = 0;
+ vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
+ ++vr_index) {
+ Normalize128(&context_ppc->vector_save.save_vr[vr_index], true);
+ }
+
+ if (minidump_->swap()) {
+ // context_ppc->context_flags was already swapped.
+ Swap(&context_ppc->srr0);
+ Swap(&context_ppc->srr1);
+ for (unsigned int gpr_index = 0;
+ gpr_index < MD_CONTEXT_PPC_GPR_COUNT;
+ ++gpr_index) {
+ Swap(&context_ppc->gpr[gpr_index]);
+ }
+ Swap(&context_ppc->cr);
+ Swap(&context_ppc->xer);
+ Swap(&context_ppc->lr);
+ Swap(&context_ppc->ctr);
+ Swap(&context_ppc->mq);
+ Swap(&context_ppc->vrsave);
+ for (unsigned int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_ppc->float_save.fpregs[fpr_index]);
+ }
+ // Don't swap context_ppc->float_save.fpscr_pad because it is only
+ // used for padding.
+ Swap(&context_ppc->float_save.fpscr);
+ for (unsigned int vr_index = 0;
+ vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
+ ++vr_index) {
+ Swap(&context_ppc->vector_save.save_vr[vr_index]);
+ }
+ Swap(&context_ppc->vector_save.save_vscr);
+ // Don't swap the padding fields in vector_save.
+ Swap(&context_ppc->vector_save.save_vrvalid);
+ }
+
+ SetContextPPC(context_ppc.release());
+
+ break;
+ }
+
+ case MD_CONTEXT_SPARC: {
+ if (expected_size != sizeof(MDRawContextSPARC)) {
+ BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " <<
+ expected_size << " != " << sizeof(MDRawContextSPARC);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC());
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_sparc->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_sparc->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_sparc.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextSPARC) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read sparc context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext sparc does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_sparc->context_flags was already swapped.
+ for (unsigned int gpr_index = 0;
+ gpr_index < MD_CONTEXT_SPARC_GPR_COUNT;
+ ++gpr_index) {
+ Swap(&context_sparc->g_r[gpr_index]);
+ }
+ Swap(&context_sparc->ccr);
+ Swap(&context_sparc->pc);
+ Swap(&context_sparc->npc);
+ Swap(&context_sparc->y);
+ Swap(&context_sparc->asi);
+ Swap(&context_sparc->fprs);
+ for (unsigned int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_sparc->float_save.regs[fpr_index]);
+ }
+ Swap(&context_sparc->float_save.filler);
+ Swap(&context_sparc->float_save.fsr);
+ }
+ SetContextSPARC(context_sparc.release());
+
+ break;
+ }
+
+ case MD_CONTEXT_ARM: {
+ if (expected_size != sizeof(MDRawContextARM)) {
+ BPLOG(ERROR) << "MinidumpContext arm size mismatch, " <<
+ expected_size << " != " << sizeof(MDRawContextARM);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextARM> context_arm(new MDRawContextARM());
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_arm->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_arm->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_arm.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextARM) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read arm context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext arm does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_arm->context_flags was already swapped.
+ for (unsigned int ireg_index = 0;
+ ireg_index < MD_CONTEXT_ARM_GPR_COUNT;
+ ++ireg_index) {
+ Swap(&context_arm->iregs[ireg_index]);
+ }
+ Swap(&context_arm->cpsr);
+ Swap(&context_arm->float_save.fpscr);
+ for (unsigned int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_arm->float_save.regs[fpr_index]);
+ }
+ for (unsigned int fpe_index = 0;
+ fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
+ ++fpe_index) {
+ Swap(&context_arm->float_save.extra[fpe_index]);
+ }
+ }
+ SetContextARM(context_arm.release());
+
+ break;
+ }
+
+ case MD_CONTEXT_MIPS:
+ case MD_CONTEXT_MIPS64: {
+ if (expected_size != sizeof(MDRawContextMIPS)) {
+ BPLOG(ERROR) << "MinidumpContext MIPS size mismatch, "
+ << expected_size
+ << " != "
+ << sizeof(MDRawContextMIPS);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextMIPS> context_mips(new MDRawContextMIPS());
+
+ // Set the context_flags member, which has already been read, and
+ // read the rest of the structure beginning with the first member
+ // after context_flags.
+ context_mips->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_mips->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_mips.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextMIPS) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read MIPS context";
+ return false;
+ }
+
+ // Do this after reading the entire MDRawContext structure because
+ // GetSystemInfo may seek minidump to a new position.
+ if (!CheckAgainstSystemInfo(cpu_type)) {
+ BPLOG(ERROR) << "MinidumpContext MIPS does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_mips->context_flags was already swapped.
+ for (int ireg_index = 0;
+ ireg_index < MD_CONTEXT_MIPS_GPR_COUNT;
+ ++ireg_index) {
+ Swap(&context_mips->iregs[ireg_index]);
+ }
+ Swap(&context_mips->mdhi);
+ Swap(&context_mips->mdlo);
+ for (int dsp_index = 0;
+ dsp_index < MD_CONTEXT_MIPS_DSP_COUNT;
+ ++dsp_index) {
+ Swap(&context_mips->hi[dsp_index]);
+ Swap(&context_mips->lo[dsp_index]);
+ }
+ Swap(&context_mips->dsp_control);
+ Swap(&context_mips->epc);
+ Swap(&context_mips->badvaddr);
+ Swap(&context_mips->status);
+ Swap(&context_mips->cause);
+ for (int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_mips->float_save.regs[fpr_index]);
+ }
+ Swap(&context_mips->float_save.fpcsr);
+ Swap(&context_mips->float_save.fir);
+ }
+ SetContextMIPS(context_mips.release());
+
+ break;
+ }
+
+ default: {
+ // Unknown context type - Don't log as an error yet. Let the
+ // caller work that out.
+ BPLOG(INFO) << "MinidumpContext unknown context type " <<
+ HexString(cpu_type);
+ return false;
+ break;
+ }
+ }
+ SetContextFlags(context_flags);
+ }
+
+ valid_ = true;
+ return true;
+}
+
+bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
+ // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM,
+ // as this function just implements a sanity check.
+ MinidumpSystemInfo* system_info = minidump_->GetSystemInfo();
+ if (!system_info) {
+ BPLOG(INFO) << "MinidumpContext could not be compared against "
+ "MinidumpSystemInfo";
+ return true;
+ }
+
+ // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info.
+ const MDRawSystemInfo* raw_system_info = system_info->system_info();
+ if (!raw_system_info) {
+ BPLOG(INFO) << "MinidumpContext could not be compared against "
+ "MDRawSystemInfo";
+ return false;
+ }
+
+ MDCPUArchitecture system_info_cpu_type = static_cast<MDCPUArchitecture>(
+ raw_system_info->processor_architecture);
+
+ // Compare the CPU type of the context record to the CPU type in the
+ // minidump's system info stream.
+ bool return_value = false;
+ switch (context_cpu_type) {
+ case MD_CONTEXT_X86:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 ||
+ system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 ||
+ system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) {
+ return_value = true;
+ }
+ break;
+
+ case MD_CONTEXT_PPC:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_PPC64:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC64)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_AMD64:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_SPARC:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_ARM:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_ARM64:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_MIPS:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
+ return_value = true;
+ break;
+
+ case MD_CONTEXT_MIPS64:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64)
+ return_value = true;
+ break;
+ }
+
+ BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
+ HexString(context_cpu_type) <<
+ " wrong for MinidumpSystemInfo CPU " <<
+ HexString(system_info_cpu_type);
+
+ return return_value;
+}
+
+
+//
+// MinidumpMemoryRegion
+//
+
+
+uint32_t MinidumpMemoryRegion::max_bytes_ = 2 * 1024 * 1024; // 2MB
+
+
+MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump)
+ : MinidumpObject(minidump),
+ descriptor_(NULL),
+ memory_(NULL) {
+}
+
+
+MinidumpMemoryRegion::~MinidumpMemoryRegion() {
+ delete memory_;
+}
+
+
+void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) {
+ descriptor_ = descriptor;
+ valid_ = descriptor &&
+ descriptor_->memory.data_size <=
+ numeric_limits<uint64_t>::max() -
+ descriptor_->start_of_memory_range;
+}
+
+
+const uint8_t* MinidumpMemoryRegion::GetMemory() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory";
+ return NULL;
+ }
+
+ if (!memory_) {
+ if (descriptor_->memory.data_size == 0) {
+ BPLOG(ERROR) << "MinidumpMemoryRegion is empty";
+ return NULL;
+ }
+
+ if (!minidump_->SeekSet(descriptor_->memory.rva)) {
+ BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region";
+ return NULL;
+ }
+
+ if (descriptor_->memory.data_size > max_bytes_) {
+ BPLOG(ERROR) << "MinidumpMemoryRegion size " <<
+ descriptor_->memory.data_size << " exceeds maximum " <<
+ max_bytes_;
+ return NULL;
+ }
+
+ scoped_ptr< vector<uint8_t> > memory(
+ new vector<uint8_t>(descriptor_->memory.data_size));
+
+ if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) {
+ BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region";
+ return NULL;
+ }
+
+ memory_ = memory.release();
+ }
+
+ return &(*memory_)[0];
+}
+
+
+uint64_t MinidumpMemoryRegion::GetBase() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase";
+ return static_cast<uint64_t>(-1);
+ }
+
+ return descriptor_->start_of_memory_range;
+}
+
+
+uint32_t MinidumpMemoryRegion::GetSize() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize";
+ return 0;
+ }
+
+ return descriptor_->memory.data_size;
+}
+
+
+void MinidumpMemoryRegion::FreeMemory() {
+ delete memory_;
+ memory_ = NULL;
+}
+
+
+template<typename T>
+bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address,
+ T* value) const {
+ BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal "
+ "requires |value|";
+ assert(value);
+ *value = 0;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for "
+ "GetMemoryAtAddressInternal";
+ return false;
+ }
+
+ // Common failure case
+ if (address < descriptor_->start_of_memory_range ||
+ sizeof(T) > numeric_limits<uint64_t>::max() - address ||
+ address + sizeof(T) > descriptor_->start_of_memory_range +
+ descriptor_->memory.data_size) {
+ BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " <<
+ HexString(address) << "+" << sizeof(T) << "/" <<
+ HexString(descriptor_->start_of_memory_range) << "+" <<
+ HexString(descriptor_->memory.data_size);
+ return false;
+ }
+
+ const uint8_t* memory = GetMemory();
+ if (!memory) {
+ // GetMemory already logged a perfectly good message.
+ return false;
+ }
+
+ // If the CPU requires memory accesses to be aligned, this can crash.
+ // x86 and ppc are able to cope, though.
+ *value = *reinterpret_cast<const T*>(
+ &memory[address - descriptor_->start_of_memory_range]);
+
+ if (minidump_->swap())
+ Swap(value);
+
+ return true;
+}
+
+
+bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
+ uint8_t* value) const {
+ return GetMemoryAtAddressInternal(address, value);
+}
+
+
+bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
+ uint16_t* value) const {
+ return GetMemoryAtAddressInternal(address, value);
+}
+
+
+bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
+ uint32_t* value) const {
+ return GetMemoryAtAddressInternal(address, value);
+}
+
+
+bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
+ uint64_t* value) const {
+ return GetMemoryAtAddressInternal(address, value);
+}
+
+
+void MinidumpMemoryRegion::Print() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data";
+ return;
+ }
+
+ const uint8_t* memory = GetMemory();
+ if (memory) {
+ printf("0x");
+ for (unsigned int byte_index = 0;
+ byte_index < descriptor_->memory.data_size;
+ byte_index++) {
+ printf("%02x", memory[byte_index]);
+ }
+ printf("\n");
+ } else {
+ printf("No memory\n");
+ }
+}
+
+
+//
+// MinidumpThread
+//
+
+
+MinidumpThread::MinidumpThread(Minidump* minidump)
+ : MinidumpObject(minidump),
+ thread_(),
+ memory_(NULL),
+ context_(NULL) {
+}
+
+
+MinidumpThread::~MinidumpThread() {
+ delete memory_;
+ delete context_;
+}
+
+
+bool MinidumpThread::Read() {
+ // Invalidate cached data.
+ delete memory_;
+ memory_ = NULL;
+ delete context_;
+ context_ = NULL;
+
+ valid_ = false;
+
+ if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) {
+ BPLOG(ERROR) << "MinidumpThread cannot read thread";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&thread_.thread_id);
+ Swap(&thread_.suspend_count);
+ Swap(&thread_.priority_class);
+ Swap(&thread_.priority);
+ Swap(&thread_.teb);
+ Swap(&thread_.stack);
+ Swap(&thread_.thread_context);
+ }
+
+ // Check for base + size overflow or undersize.
+ if (thread_.stack.memory.rva == 0 ||
+ thread_.stack.memory.data_size == 0 ||
+ thread_.stack.memory.data_size > numeric_limits<uint64_t>::max() -
+ thread_.stack.start_of_memory_range) {
+ // This is ok, but log an error anyway.
+ BPLOG(ERROR) << "MinidumpThread has a memory region problem, " <<
+ HexString(thread_.stack.start_of_memory_range) << "+" <<
+ HexString(thread_.stack.memory.data_size) <<
+ ", RVA 0x" << HexString(thread_.stack.memory.rva);
+ } else {
+ memory_ = new MinidumpMemoryRegion(minidump_);
+ memory_->SetDescriptor(&thread_.stack);
+ }
+
+ valid_ = true;
+ return true;
+}
+
+uint64_t MinidumpThread::GetStartOfStackMemoryRange() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "GetStartOfStackMemoryRange: Invalid MinidumpThread";
+ return 0;
+ }
+
+ return thread_.stack.start_of_memory_range;
+}
+
+MinidumpMemoryRegion* MinidumpThread::GetMemory() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory";
+ return NULL;
+ }
+
+ return memory_;
+}
+
+
+MinidumpContext* MinidumpThread::GetContext() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpThread for GetContext";
+ return NULL;
+ }
+
+ if (!context_) {
+ if (!minidump_->SeekSet(thread_.thread_context.rva)) {
+ BPLOG(ERROR) << "MinidumpThread cannot seek to context";
+ return NULL;
+ }
+
+ scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
+
+ if (!context->Read(thread_.thread_context.data_size)) {
+ BPLOG(ERROR) << "MinidumpThread cannot read context";
+ return NULL;
+ }
+
+ context_ = context.release();
+ }
+
+ return context_;
+}
+
+
+bool MinidumpThread::GetThreadID(uint32_t *thread_id) const {
+ BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires "
+ "|thread_id|";
+ assert(thread_id);
+ *thread_id = 0;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID";
+ return false;
+ }
+
+ *thread_id = thread_.thread_id;
+ return true;
+}
+
+
+void MinidumpThread::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpThread cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawThread\n");
+ printf(" thread_id = 0x%x\n", thread_.thread_id);
+ printf(" suspend_count = %d\n", thread_.suspend_count);
+ printf(" priority_class = 0x%x\n", thread_.priority_class);
+ printf(" priority = 0x%x\n", thread_.priority);
+ printf(" teb = 0x%" PRIx64 "\n", thread_.teb);
+ printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n",
+ thread_.stack.start_of_memory_range);
+ printf(" stack.memory.data_size = 0x%x\n",
+ thread_.stack.memory.data_size);
+ printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva);
+ printf(" thread_context.data_size = 0x%x\n",
+ thread_.thread_context.data_size);
+ printf(" thread_context.rva = 0x%x\n",
+ thread_.thread_context.rva);
+
+ MinidumpContext* context = GetContext();
+ if (context) {
+ printf("\n");
+ context->Print();
+ } else {
+ printf(" (no context)\n");
+ printf("\n");
+ }
+
+ MinidumpMemoryRegion* memory = GetMemory();
+ if (memory) {
+ printf("Stack\n");
+ memory->Print();
+ } else {
+ printf("No stack\n");
+ }
+ printf("\n");
+}
+
+
+//
+// MinidumpThreadList
+//
+
+
+uint32_t MinidumpThreadList::max_threads_ = 4096;
+
+
+MinidumpThreadList::MinidumpThreadList(Minidump* minidump)
+ : MinidumpStream(minidump),
+ id_to_thread_map_(),
+ threads_(NULL),
+ thread_count_(0) {
+}
+
+
+MinidumpThreadList::~MinidumpThreadList() {
+ delete threads_;
+}
+
+
+bool MinidumpThreadList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ id_to_thread_map_.clear();
+ delete threads_;
+ threads_ = NULL;
+ thread_count_ = 0;
+
+ valid_ = false;
+
+ uint32_t thread_count;
+ if (expected_size < sizeof(thread_count)) {
+ BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " <<
+ expected_size << " < " << sizeof(thread_count);
+ return false;
+ }
+ if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) {
+ BPLOG(ERROR) << "MinidumpThreadList cannot read thread count";
+ return false;
+ }
+
+ if (minidump_->swap())
+ Swap(&thread_count);
+
+ if (thread_count > numeric_limits<uint32_t>::max() / sizeof(MDRawThread)) {
+ BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count <<
+ " would cause multiplication overflow";
+ return false;
+ }
+
+ if (expected_size != sizeof(thread_count) +
+ thread_count * sizeof(MDRawThread)) {
+ // may be padded with 4 bytes on 64bit ABIs for alignment
+ if (expected_size == sizeof(thread_count) + 4 +
+ thread_count * sizeof(MDRawThread)) {
+ uint32_t useless;
+ if (!minidump_->ReadBytes(&useless, 4)) {
+ BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded "
+ "bytes";
+ return false;
+ }
+ } else {
+ BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size <<
+ " != " << sizeof(thread_count) +
+ thread_count * sizeof(MDRawThread);
+ return false;
+ }
+ }
+
+
+ if (thread_count > max_threads_) {
+ BPLOG(ERROR) << "MinidumpThreadList count " << thread_count <<
+ " exceeds maximum " << max_threads_;
+ return false;
+ }
+
+ if (thread_count != 0) {
+ scoped_ptr<MinidumpThreads> threads(
+ new MinidumpThreads(thread_count, MinidumpThread(minidump_)));
+
+ for (unsigned int thread_index = 0;
+ thread_index < thread_count;
+ ++thread_index) {
+ MinidumpThread* thread = &(*threads)[thread_index];
+
+ // Assume that the file offset is correct after the last read.
+ if (!thread->Read()) {
+ BPLOG(ERROR) << "MinidumpThreadList cannot read thread " <<
+ thread_index << "/" << thread_count;
+ return false;
+ }
+
+ uint32_t thread_id;
+ if (!thread->GetThreadID(&thread_id)) {
+ BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " <<
+ thread_index << "/" << thread_count;
+ return false;
+ }
+
+ if (GetThreadByID(thread_id)) {
+ // Another thread with this ID is already in the list. Data error.
+ BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " <<
+ HexString(thread_id) << " at thread " <<
+ thread_index << "/" << thread_count;
+ return false;
+ }
+ id_to_thread_map_[thread_id] = thread;
+ }
+
+ threads_ = threads.release();
+ }
+
+ thread_count_ = thread_count;
+
+ valid_ = true;
+ return true;
+}
+
+
+MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index)
+ const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex";
+ return NULL;
+ }
+
+ if (index >= thread_count_) {
+ BPLOG(ERROR) << "MinidumpThreadList index out of range: " <<
+ index << "/" << thread_count_;
+ return NULL;
+ }
+
+ return &(*threads_)[index];
+}
+
+
+MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) {
+ // Don't check valid_. Read calls this method before everything is
+ // validated. It is safe to not check valid_ here.
+ return id_to_thread_map_[thread_id];
+}
+
+
+void MinidumpThreadList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data";
+ return;
+ }
+
+ printf("MinidumpThreadList\n");
+ printf(" thread_count = %d\n", thread_count_);
+ printf("\n");
+
+ for (unsigned int thread_index = 0;
+ thread_index < thread_count_;
+ ++thread_index) {
+ printf("thread[%d]\n", thread_index);
+
+ (*threads_)[thread_index].Print();
+ }
+}
+
+
+//
+// MinidumpModule
+//
+
+
+uint32_t MinidumpModule::max_cv_bytes_ = 32768;
+uint32_t MinidumpModule::max_misc_bytes_ = 32768;
+
+
+MinidumpModule::MinidumpModule(Minidump* minidump)
+ : MinidumpObject(minidump),
+ module_valid_(false),
+ has_debug_info_(false),
+ module_(),
+ name_(NULL),
+ cv_record_(NULL),
+ cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE),
+ misc_record_(NULL) {
+}
+
+
+MinidumpModule::~MinidumpModule() {
+ delete name_;
+ delete cv_record_;
+ delete misc_record_;
+}
+
+
+bool MinidumpModule::Read() {
+ // Invalidate cached data.
+ delete name_;
+ name_ = NULL;
+ delete cv_record_;
+ cv_record_ = NULL;
+ cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE;
+ delete misc_record_;
+ misc_record_ = NULL;
+
+ module_valid_ = false;
+ has_debug_info_ = false;
+ valid_ = false;
+
+ if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) {
+ BPLOG(ERROR) << "MinidumpModule cannot read module";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&module_.base_of_image);
+ Swap(&module_.size_of_image);
+ Swap(&module_.checksum);
+ Swap(&module_.time_date_stamp);
+ Swap(&module_.module_name_rva);
+ Swap(&module_.version_info.signature);
+ Swap(&module_.version_info.struct_version);
+ Swap(&module_.version_info.file_version_hi);
+ Swap(&module_.version_info.file_version_lo);
+ Swap(&module_.version_info.product_version_hi);
+ Swap(&module_.version_info.product_version_lo);
+ Swap(&module_.version_info.file_flags_mask);
+ Swap(&module_.version_info.file_flags);
+ Swap(&module_.version_info.file_os);
+ Swap(&module_.version_info.file_type);
+ Swap(&module_.version_info.file_subtype);
+ Swap(&module_.version_info.file_date_hi);
+ Swap(&module_.version_info.file_date_lo);
+ Swap(&module_.cv_record);
+ Swap(&module_.misc_record);
+ // Don't swap reserved fields because their contents are unknown (as
+ // are their proper widths).
+ }
+
+ // Check for base + size overflow or undersize.
+ if (module_.size_of_image == 0 ||
+ module_.size_of_image >
+ numeric_limits<uint64_t>::max() - module_.base_of_image) {
+ BPLOG(ERROR) << "MinidumpModule has a module problem, " <<
+ HexString(module_.base_of_image) << "+" <<
+ HexString(module_.size_of_image);
+ return false;
+ }
+
+ module_valid_ = true;
+ return true;
+}
+
+
+bool MinidumpModule::ReadAuxiliaryData() {
+ if (!module_valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData";
+ return false;
+ }
+
+ // Each module must have a name.
+ name_ = minidump_->ReadString(module_.module_name_rva);
+ if (!name_) {
+ BPLOG(ERROR) << "MinidumpModule could not read name";
+ return false;
+ }
+
+ // At this point, we have enough info for the module to be valid.
+ valid_ = true;
+
+ // CodeView and miscellaneous debug records are only required if the
+ // module indicates that they exist.
+ if (module_.cv_record.data_size && !GetCVRecord(NULL)) {
+ BPLOG(ERROR) << "MinidumpModule has no CodeView record, "
+ "but one was expected";
+ return false;
+ }
+
+ if (module_.misc_record.data_size && !GetMiscRecord(NULL)) {
+ BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, "
+ "but one was expected";
+ return false;
+ }
+
+ has_debug_info_ = true;
+ return true;
+}
+
+
+string MinidumpModule::code_file() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for code_file";
+ return "";
+ }
+
+ return *name_;
+}
+
+
+string MinidumpModule::code_identifier() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier";
+ return "";
+ }
+
+ if (!has_debug_info_)
+ return "";
+
+ MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo();
+ if (!minidump_system_info) {
+ BPLOG(ERROR) << "MinidumpModule code_identifier requires "
+ "MinidumpSystemInfo";
+ return "";
+ }
+
+ const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info();
+ if (!raw_system_info) {
+ BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo";
+ return "";
+ }
+
+ string identifier;
+
+ switch (raw_system_info->platform_id) {
+ case MD_OS_WIN32_NT:
+ case MD_OS_WIN32_WINDOWS: {
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ char identifier_string[17];
+ snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
+ module_.time_date_stamp, module_.size_of_image);
+ identifier = identifier_string;
+ break;
+ }
+
+ case MD_OS_ANDROID:
+ case MD_OS_LINUX: {
+ // If ELF CodeView data is present, return the debug id.
+ if (cv_record_ && cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
+ const MDCVInfoELF* cv_record_elf =
+ reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
+ assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
+
+ for (unsigned int build_id_index = 0;
+ build_id_index < (cv_record_->size() - MDCVInfoELF_minsize);
+ ++build_id_index) {
+ char hexbyte[3];
+ snprintf(hexbyte, sizeof(hexbyte), "%02x",
+ cv_record_elf->build_id[build_id_index]);
+ identifier += hexbyte;
+ }
+ break;
+ }
+ // Otherwise fall through to the case below.
+ }
+
+ case MD_OS_MAC_OS_X:
+ case MD_OS_IOS:
+ case MD_OS_SOLARIS:
+ case MD_OS_NACL:
+ case MD_OS_PS3: {
+ // TODO(mmentovai): support uuid extension if present, otherwise fall
+ // back to version (from LC_ID_DYLIB?), otherwise fall back to something
+ // else.
+ identifier = "id";
+ break;
+ }
+
+ default: {
+ // Without knowing what OS generated the dump, we can't generate a good
+ // identifier. Return an empty string, signalling failure.
+ BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, "
+ "found " << HexString(raw_system_info->platform_id);
+ break;
+ }
+ }
+
+ return identifier;
+}
+
+
+string MinidumpModule::debug_file() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for debug_file";
+ return "";
+ }
+
+ if (!has_debug_info_)
+ return "";
+
+ string file;
+ // Prefer the CodeView record if present.
+ if (cv_record_) {
+ if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
+ // It's actually an MDCVInfoPDB70 structure.
+ const MDCVInfoPDB70* cv_record_70 =
+ reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
+ assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
+
+ // GetCVRecord guarantees pdb_file_name is null-terminated.
+ file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name);
+ } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
+ // It's actually an MDCVInfoPDB20 structure.
+ const MDCVInfoPDB20* cv_record_20 =
+ reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
+ assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
+
+ // GetCVRecord guarantees pdb_file_name is null-terminated.
+ file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name);
+ } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
+ // It's actually an MDCVInfoELF structure.
+ assert(reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0])->
+ cv_signature == MD_CVINFOELF_SIGNATURE);
+
+ // For MDCVInfoELF, the debug file is the code file.
+ file = *name_;
+ }
+
+ // If there's a CodeView record but it doesn't match a known signature,
+ // try the miscellaneous record.
+ }
+
+ if (file.empty()) {
+ // No usable CodeView record. Try the miscellaneous debug record.
+ if (misc_record_) {
+ const MDImageDebugMisc* misc_record =
+ reinterpret_cast<const MDImageDebugMisc *>(&(*misc_record_)[0]);
+ if (!misc_record->unicode) {
+ // If it's not Unicode, just stuff it into the string. It's unclear
+ // if misc_record->data is 0-terminated, so use an explicit size.
+ file = string(
+ reinterpret_cast<const char*>(misc_record->data),
+ module_.misc_record.data_size - MDImageDebugMisc_minsize);
+ } else {
+ // There's a misc_record but it encodes the debug filename in UTF-16.
+ // (Actually, because miscellaneous records are so old, it's probably
+ // UCS-2.) Convert it to UTF-8 for congruity with the other strings
+ // that this method (and all other methods in the Minidump family)
+ // return.
+
+ unsigned int bytes =
+ module_.misc_record.data_size - MDImageDebugMisc_minsize;
+ if (bytes % 2 == 0) {
+ unsigned int utf16_words = bytes / 2;
+
+ // UTF16ToUTF8 expects a vector<uint16_t>, so create a temporary one
+ // and copy the UTF-16 data into it.
+ vector<uint16_t> string_utf16(utf16_words);
+ if (utf16_words)
+ memcpy(&string_utf16[0], &misc_record->data, bytes);
+
+ // GetMiscRecord already byte-swapped the data[] field if it contains
+ // UTF-16, so pass false as the swap argument.
+ scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false));
+ file = *new_file;
+ }
+ }
+ }
+ }
+
+ // Relatively common case
+ BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine "
+ "debug_file for " << *name_;
+
+ return file;
+}
+
+static string guid_and_age_to_debug_id(const MDGUID& guid,
+ uint32_t age) {
+ char identifier_string[41];
+ snprintf(identifier_string, sizeof(identifier_string),
+ "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
+ guid.data1,
+ guid.data2,
+ guid.data3,
+ guid.data4[0],
+ guid.data4[1],
+ guid.data4[2],
+ guid.data4[3],
+ guid.data4[4],
+ guid.data4[5],
+ guid.data4[6],
+ guid.data4[7],
+ age);
+ return identifier_string;
+}
+
+string MinidumpModule::debug_identifier() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier";
+ return "";
+ }
+
+ if (!has_debug_info_)
+ return "";
+
+ string identifier;
+
+ // Use the CodeView record if present.
+ if (cv_record_) {
+ if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
+ // It's actually an MDCVInfoPDB70 structure.
+ const MDCVInfoPDB70* cv_record_70 =
+ reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
+ assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
+
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ identifier = guid_and_age_to_debug_id(cv_record_70->signature,
+ cv_record_70->age);
+ } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
+ // It's actually an MDCVInfoPDB20 structure.
+ const MDCVInfoPDB20* cv_record_20 =
+ reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
+ assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
+
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ char identifier_string[17];
+ snprintf(identifier_string, sizeof(identifier_string),
+ "%08X%x", cv_record_20->signature, cv_record_20->age);
+ identifier = identifier_string;
+ } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
+ // It's actually an MDCVInfoELF structure.
+ const MDCVInfoELF* cv_record_elf =
+ reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
+ assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
+
+ // For backwards-compatibility, stuff as many bytes as will fit into
+ // a MDGUID and use the MS symbol server format as MDCVInfoPDB70 does
+ // with age = 0. Historically Breakpad would do this during dump
+ // writing to fit the build id data into a MDCVInfoPDB70 struct.
+ // The full build id is available by calling code_identifier.
+ MDGUID guid = {0};
+ memcpy(&guid, &cv_record_elf->build_id,
+ std::min(cv_record_->size() - MDCVInfoELF_minsize,
+ sizeof(MDGUID)));
+ identifier = guid_and_age_to_debug_id(guid, 0);
+ }
+ }
+
+ // TODO(mmentovai): if there's no usable CodeView record, there might be a
+ // miscellaneous debug record. It only carries a filename, though, and no
+ // identifier. I'm not sure what the right thing to do for the identifier
+ // is in that case, but I don't expect to find many modules without a
+ // CodeView record (or some other Breakpad extension structure in place of
+ // a CodeView record). Treat it as an error (empty identifier) for now.
+
+ // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier().
+
+ // Relatively common case
+ BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine "
+ "debug_identifier for " << *name_;
+
+ return identifier;
+}
+
+
+string MinidumpModule::version() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for version";
+ return "";
+ }
+
+ string version;
+
+ if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE &&
+ module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) {
+ char version_string[24];
+ snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u",
+ module_.version_info.file_version_hi >> 16,
+ module_.version_info.file_version_hi & 0xffff,
+ module_.version_info.file_version_lo >> 16,
+ module_.version_info.file_version_lo & 0xffff);
+ version = version_string;
+ }
+
+ // TODO(mmentovai): possibly support other struct types in place of
+ // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use
+ // a different structure that better represents versioning facilities on
+ // Mac OS X and Linux, instead of forcing them to adhere to the dotted
+ // quad of 16-bit ints that Windows uses.
+
+ BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine "
+ "version for " << *name_;
+
+ return version;
+}
+
+
+CodeModule* MinidumpModule::Copy() const {
+ return new BasicCodeModule(this);
+}
+
+
+uint64_t MinidumpModule::shrink_down_delta() const {
+ return 0;
+}
+
+void MinidumpModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
+ // Not implemented
+ assert(false);
+}
+
+
+const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) {
+ if (!module_valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord";
+ return NULL;
+ }
+
+ if (!cv_record_) {
+ // This just guards against 0-sized CodeView records; more specific checks
+ // are used when the signature is checked against various structure types.
+ if (module_.cv_record.data_size == 0) {
+ return NULL;
+ }
+
+ if (!minidump_->SeekSet(module_.cv_record.rva)) {
+ BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record";
+ return NULL;
+ }
+
+ if (module_.cv_record.data_size > max_cv_bytes_) {
+ BPLOG(ERROR) << "MinidumpModule CodeView record size " <<
+ module_.cv_record.data_size << " exceeds maximum " <<
+ max_cv_bytes_;
+ return NULL;
+ }
+
+ // Allocating something that will be accessed as MDCVInfoPDB70 or
+ // MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment
+ // problems. x86 and ppc are able to cope, though. This allocation
+ // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are
+ // variable-sized due to their pdb_file_name fields; these structures
+ // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating
+ // them as such would result in incomplete structures or overruns.
+ scoped_ptr< vector<uint8_t> > cv_record(
+ new vector<uint8_t>(module_.cv_record.data_size));
+
+ if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) {
+ BPLOG(ERROR) << "MinidumpModule could not read CodeView record";
+ return NULL;
+ }
+
+ uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE;
+ if (module_.cv_record.data_size > sizeof(signature)) {
+ MDCVInfoPDB70* cv_record_signature =
+ reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
+ signature = cv_record_signature->cv_signature;
+ if (minidump_->swap())
+ Swap(&signature);
+ }
+
+ if (signature == MD_CVINFOPDB70_SIGNATURE) {
+ // Now that the structure type is known, recheck the size.
+ if (MDCVInfoPDB70_minsize > module_.cv_record.data_size) {
+ BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " <<
+ MDCVInfoPDB70_minsize << " > " <<
+ module_.cv_record.data_size;
+ return NULL;
+ }
+
+ if (minidump_->swap()) {
+ MDCVInfoPDB70* cv_record_70 =
+ reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
+ Swap(&cv_record_70->cv_signature);
+ Swap(&cv_record_70->signature);
+ Swap(&cv_record_70->age);
+ // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit
+ // quantities. (It's a path, is it UTF-8?)
+ }
+
+ // The last field of either structure is null-terminated 8-bit character
+ // data. Ensure that it's null-terminated.
+ if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
+ BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not "
+ "0-terminated";
+ return NULL;
+ }
+ } else if (signature == MD_CVINFOPDB20_SIGNATURE) {
+ // Now that the structure type is known, recheck the size.
+ if (MDCVInfoPDB20_minsize > module_.cv_record.data_size) {
+ BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " <<
+ MDCVInfoPDB20_minsize << " > " <<
+ module_.cv_record.data_size;
+ return NULL;
+ }
+ if (minidump_->swap()) {
+ MDCVInfoPDB20* cv_record_20 =
+ reinterpret_cast<MDCVInfoPDB20*>(&(*cv_record)[0]);
+ Swap(&cv_record_20->cv_header.signature);
+ Swap(&cv_record_20->cv_header.offset);
+ Swap(&cv_record_20->signature);
+ Swap(&cv_record_20->age);
+ // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit
+ // quantities. (It's a path, is it UTF-8?)
+ }
+
+ // The last field of either structure is null-terminated 8-bit character
+ // data. Ensure that it's null-terminated.
+ if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
+ BPLOG(ERROR) << "MindumpModule CodeView2 record string is not "
+ "0-terminated";
+ return NULL;
+ }
+ } else if (signature == MD_CVINFOELF_SIGNATURE) {
+ // Now that the structure type is known, recheck the size.
+ if (MDCVInfoELF_minsize > module_.cv_record.data_size) {
+ BPLOG(ERROR) << "MinidumpModule CodeViewELF record size mismatch, " <<
+ MDCVInfoELF_minsize << " > " <<
+ module_.cv_record.data_size;
+ return NULL;
+ }
+ // There's nothing to swap in CVInfoELF, it's just raw bytes.
+ }
+
+ // If the signature doesn't match something above, it's not something
+ // that Breakpad can presently handle directly. Because some modules in
+ // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE,
+ // don't bail out here - allow the data to be returned to the user,
+ // although byte-swapping can't be done.
+
+ // Store the vector type because that's how storage was allocated, but
+ // return it casted to uint8_t*.
+ cv_record_ = cv_record.release();
+ cv_record_signature_ = signature;
+ }
+
+ if (size)
+ *size = module_.cv_record.data_size;
+
+ return &(*cv_record_)[0];
+}
+
+
+const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) {
+ if (!module_valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord";
+ return NULL;
+ }
+
+ if (!misc_record_) {
+ if (module_.misc_record.data_size == 0) {
+ return NULL;
+ }
+
+ if (MDImageDebugMisc_minsize > module_.misc_record.data_size) {
+ BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record "
+ "size mismatch, " << MDImageDebugMisc_minsize << " > " <<
+ module_.misc_record.data_size;
+ return NULL;
+ }
+
+ if (!minidump_->SeekSet(module_.misc_record.rva)) {
+ BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous "
+ "debugging record";
+ return NULL;
+ }
+
+ if (module_.misc_record.data_size > max_misc_bytes_) {
+ BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " <<
+ module_.misc_record.data_size << " exceeds maximum " <<
+ max_misc_bytes_;
+ return NULL;
+ }
+
+ // Allocating something that will be accessed as MDImageDebugMisc but
+ // is allocated as uint8_t[] can cause alignment problems. x86 and
+ // ppc are able to cope, though. This allocation style is needed
+ // because the MDImageDebugMisc is variable-sized due to its data field;
+ // this structure is not MDImageDebugMisc_minsize and treating it as such
+ // would result in an incomplete structure or an overrun.
+ scoped_ptr< vector<uint8_t> > misc_record_mem(
+ new vector<uint8_t>(module_.misc_record.data_size));
+ MDImageDebugMisc* misc_record =
+ reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]);
+
+ if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) {
+ BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging "
+ "record";
+ return NULL;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&misc_record->data_type);
+ Swap(&misc_record->length);
+ // Don't swap misc_record.unicode because it's an 8-bit quantity.
+ // Don't swap the reserved fields for the same reason, and because
+ // they don't contain any valid data.
+ if (misc_record->unicode) {
+ // There is a potential alignment problem, but shouldn't be a problem
+ // in practice due to the layout of MDImageDebugMisc.
+ uint16_t* data16 = reinterpret_cast<uint16_t*>(&(misc_record->data));
+ unsigned int dataBytes = module_.misc_record.data_size -
+ MDImageDebugMisc_minsize;
+ Swap(data16, dataBytes);
+ }
+ }
+
+ if (module_.misc_record.data_size != misc_record->length) {
+ BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data "
+ "size mismatch, " << module_.misc_record.data_size <<
+ " != " << misc_record->length;
+ return NULL;
+ }
+
+ // Store the vector type because that's how storage was allocated, but
+ // return it casted to MDImageDebugMisc*.
+ misc_record_ = misc_record_mem.release();
+ }
+
+ if (size)
+ *size = module_.misc_record.data_size;
+
+ return reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_)[0]);
+}
+
+
+void MinidumpModule::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpModule cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawModule\n");
+ printf(" base_of_image = 0x%" PRIx64 "\n",
+ module_.base_of_image);
+ printf(" size_of_image = 0x%x\n",
+ module_.size_of_image);
+ printf(" checksum = 0x%x\n",
+ module_.checksum);
+ printf(" time_date_stamp = 0x%x %s\n",
+ module_.time_date_stamp,
+ TimeTToUTCString(module_.time_date_stamp).c_str());
+ printf(" module_name_rva = 0x%x\n",
+ module_.module_name_rva);
+ printf(" version_info.signature = 0x%x\n",
+ module_.version_info.signature);
+ printf(" version_info.struct_version = 0x%x\n",
+ module_.version_info.struct_version);
+ printf(" version_info.file_version = 0x%x:0x%x\n",
+ module_.version_info.file_version_hi,
+ module_.version_info.file_version_lo);
+ printf(" version_info.product_version = 0x%x:0x%x\n",
+ module_.version_info.product_version_hi,
+ module_.version_info.product_version_lo);
+ printf(" version_info.file_flags_mask = 0x%x\n",
+ module_.version_info.file_flags_mask);
+ printf(" version_info.file_flags = 0x%x\n",
+ module_.version_info.file_flags);
+ printf(" version_info.file_os = 0x%x\n",
+ module_.version_info.file_os);
+ printf(" version_info.file_type = 0x%x\n",
+ module_.version_info.file_type);
+ printf(" version_info.file_subtype = 0x%x\n",
+ module_.version_info.file_subtype);
+ printf(" version_info.file_date = 0x%x:0x%x\n",
+ module_.version_info.file_date_hi,
+ module_.version_info.file_date_lo);
+ printf(" cv_record.data_size = %d\n",
+ module_.cv_record.data_size);
+ printf(" cv_record.rva = 0x%x\n",
+ module_.cv_record.rva);
+ printf(" misc_record.data_size = %d\n",
+ module_.misc_record.data_size);
+ printf(" misc_record.rva = 0x%x\n",
+ module_.misc_record.rva);
+
+ printf(" (code_file) = \"%s\"\n", code_file().c_str());
+ printf(" (code_identifier) = \"%s\"\n",
+ code_identifier().c_str());
+
+ uint32_t cv_record_size;
+ const uint8_t *cv_record = GetCVRecord(&cv_record_size);
+ if (cv_record) {
+ if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
+ const MDCVInfoPDB70* cv_record_70 =
+ reinterpret_cast<const MDCVInfoPDB70*>(cv_record);
+ assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
+
+ printf(" (cv_record).cv_signature = 0x%x\n",
+ cv_record_70->cv_signature);
+ printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-",
+ cv_record_70->signature.data1,
+ cv_record_70->signature.data2,
+ cv_record_70->signature.data3,
+ cv_record_70->signature.data4[0],
+ cv_record_70->signature.data4[1]);
+ for (unsigned int guidIndex = 2;
+ guidIndex < 8;
+ ++guidIndex) {
+ printf("%02x", cv_record_70->signature.data4[guidIndex]);
+ }
+ printf("\n");
+ printf(" (cv_record).age = %d\n",
+ cv_record_70->age);
+ printf(" (cv_record).pdb_file_name = \"%s\"\n",
+ cv_record_70->pdb_file_name);
+ } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
+ const MDCVInfoPDB20* cv_record_20 =
+ reinterpret_cast<const MDCVInfoPDB20*>(cv_record);
+ assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
+
+ printf(" (cv_record).cv_header.signature = 0x%x\n",
+ cv_record_20->cv_header.signature);
+ printf(" (cv_record).cv_header.offset = 0x%x\n",
+ cv_record_20->cv_header.offset);
+ printf(" (cv_record).signature = 0x%x %s\n",
+ cv_record_20->signature,
+ TimeTToUTCString(cv_record_20->signature).c_str());
+ printf(" (cv_record).age = %d\n",
+ cv_record_20->age);
+ printf(" (cv_record).pdb_file_name = \"%s\"\n",
+ cv_record_20->pdb_file_name);
+ } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
+ const MDCVInfoELF* cv_record_elf =
+ reinterpret_cast<const MDCVInfoELF*>(cv_record);
+ assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
+
+ printf(" (cv_record).cv_signature = 0x%x\n",
+ cv_record_elf->cv_signature);
+ printf(" (cv_record).build_id = ");
+ for (unsigned int build_id_index = 0;
+ build_id_index < (cv_record_size - MDCVInfoELF_minsize);
+ ++build_id_index) {
+ printf("%02x", cv_record_elf->build_id[build_id_index]);
+ }
+ printf("\n");
+ } else {
+ printf(" (cv_record) = ");
+ for (unsigned int cv_byte_index = 0;
+ cv_byte_index < cv_record_size;
+ ++cv_byte_index) {
+ printf("%02x", cv_record[cv_byte_index]);
+ }
+ printf("\n");
+ }
+ } else {
+ printf(" (cv_record) = (null)\n");
+ }
+
+ const MDImageDebugMisc* misc_record = GetMiscRecord(NULL);
+ if (misc_record) {
+ printf(" (misc_record).data_type = 0x%x\n",
+ misc_record->data_type);
+ printf(" (misc_record).length = 0x%x\n",
+ misc_record->length);
+ printf(" (misc_record).unicode = %d\n",
+ misc_record->unicode);
+ if (misc_record->unicode) {
+ string misc_record_data_utf8;
+ ConvertUTF16BufferToUTF8String(
+ reinterpret_cast<const uint16_t*>(misc_record->data),
+ misc_record->length - offsetof(MDImageDebugMisc, data),
+ &misc_record_data_utf8,
+ false); // already swapped
+ printf(" (misc_record).data = \"%s\"\n",
+ misc_record_data_utf8.c_str());
+ } else {
+ printf(" (misc_record).data = \"%s\"\n",
+ misc_record->data);
+ }
+ } else {
+ printf(" (misc_record) = (null)\n");
+ }
+
+ printf(" (debug_file) = \"%s\"\n", debug_file().c_str());
+ printf(" (debug_identifier) = \"%s\"\n",
+ debug_identifier().c_str());
+ printf(" (version) = \"%s\"\n", version().c_str());
+ printf("\n");
+}
+
+
+//
+// MinidumpModuleList
+//
+
+
+uint32_t MinidumpModuleList::max_modules_ = 1024;
+
+
+MinidumpModuleList::MinidumpModuleList(Minidump* minidump)
+ : MinidumpStream(minidump),
+ range_map_(new RangeMap<uint64_t, unsigned int>()),
+ modules_(NULL),
+ module_count_(0) {
+ range_map_->SetEnableShrinkDown(minidump_->IsAndroid());
+}
+
+
+MinidumpModuleList::~MinidumpModuleList() {
+ delete range_map_;
+ delete modules_;
+}
+
+
+bool MinidumpModuleList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ range_map_->Clear();
+ delete modules_;
+ modules_ = NULL;
+ module_count_ = 0;
+
+ valid_ = false;
+
+ uint32_t module_count;
+ if (expected_size < sizeof(module_count)) {
+ BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " <<
+ expected_size << " < " << sizeof(module_count);
+ return false;
+ }
+ if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) {
+ BPLOG(ERROR) << "MinidumpModuleList could not read module count";
+ return false;
+ }
+
+ if (minidump_->swap())
+ Swap(&module_count);
+
+ if (module_count > numeric_limits<uint32_t>::max() / MD_MODULE_SIZE) {
+ BPLOG(ERROR) << "MinidumpModuleList module count " << module_count <<
+ " would cause multiplication overflow";
+ return false;
+ }
+
+ if (expected_size != sizeof(module_count) +
+ module_count * MD_MODULE_SIZE) {
+ // may be padded with 4 bytes on 64bit ABIs for alignment
+ if (expected_size == sizeof(module_count) + 4 +
+ module_count * MD_MODULE_SIZE) {
+ uint32_t useless;
+ if (!minidump_->ReadBytes(&useless, 4)) {
+ BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded "
+ "bytes";
+ return false;
+ }
+ } else {
+ BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size <<
+ " != " << sizeof(module_count) +
+ module_count * MD_MODULE_SIZE;
+ return false;
+ }
+ }
+
+ if (module_count > max_modules_) {
+ BPLOG(ERROR) << "MinidumpModuleList count " << module_count_ <<
+ " exceeds maximum " << max_modules_;
+ return false;
+ }
+
+ if (module_count != 0) {
+ scoped_ptr<MinidumpModules> modules(
+ new MinidumpModules(module_count, MinidumpModule(minidump_)));
+
+ for (unsigned int module_index = 0;
+ module_index < module_count;
+ ++module_index) {
+ MinidumpModule* module = &(*modules)[module_index];
+
+ // Assume that the file offset is correct after the last read.
+ if (!module->Read()) {
+ BPLOG(ERROR) << "MinidumpModuleList could not read module " <<
+ module_index << "/" << module_count;
+ return false;
+ }
+ }
+
+ // Loop through the module list once more to read additional data and
+ // build the range map. This is done in a second pass because
+ // MinidumpModule::ReadAuxiliaryData seeks around, and if it were
+ // included in the loop above, additional seeks would be needed where
+ // none are now to read contiguous data.
+ uint64_t last_end_address = 0;
+ for (unsigned int module_index = 0;
+ module_index < module_count;
+ ++module_index) {
+ MinidumpModule* module = &(*modules)[module_index];
+
+ // ReadAuxiliaryData fails if any data that the module indicates should
+ // exist is missing, but we treat some such cases as valid anyway. See
+ // issue #222: if a debugging record is of a format that's too large to
+ // handle, it shouldn't render the entire dump invalid. Check module
+ // validity before giving up.
+ if (!module->ReadAuxiliaryData() && !module->valid()) {
+ BPLOG(ERROR) << "MinidumpModuleList could not read required module "
+ "auxiliary data for module " <<
+ module_index << "/" << module_count;
+ return false;
+ }
+
+ // It is safe to use module->code_file() after successfully calling
+ // module->ReadAuxiliaryData or noting that the module is valid.
+
+ uint64_t base_address = module->base_address();
+ uint64_t module_size = module->size();
+ if (base_address == static_cast<uint64_t>(-1)) {
+ BPLOG(ERROR) << "MinidumpModuleList found bad base address "
+ "for module " << module_index << "/" << module_count <<
+ ", " << module->code_file();
+ return false;
+ }
+
+ if (!range_map_->StoreRange(base_address, module_size, module_index)) {
+ // Android's shared memory implementation /dev/ashmem can contain
+ // duplicate entries for JITted code, so ignore these.
+ // TODO(wfh): Remove this code when Android is fixed.
+ // See https://crbug.com/439531
+ const string kDevAshmem("/dev/ashmem/");
+ if (module->code_file().compare(
+ 0, kDevAshmem.length(), kDevAshmem) != 0) {
+ if (base_address < last_end_address) {
+ // If failed due to apparent range overlap the cause may be
+ // the client correction applied for Android packed relocations.
+ // If this is the case, back out the client correction and retry.
+ module_size -= last_end_address - base_address;
+ base_address = last_end_address;
+ if (!range_map_->StoreRange(base_address,
+ module_size, module_index)) {
+ BPLOG(ERROR) << "MinidumpModuleList could not store module " <<
+ module_index << "/" << module_count << ", " <<
+ module->code_file() << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(module_size) << ", after adjusting";
+ return false;
+ }
+ } else {
+ BPLOG(ERROR) << "MinidumpModuleList could not store module " <<
+ module_index << "/" << module_count << ", " <<
+ module->code_file() << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(module_size);
+ return false;
+ }
+ } else {
+ BPLOG(INFO) << "MinidumpModuleList ignoring overlapping module " <<
+ module_index << "/" << module_count << ", " <<
+ module->code_file() << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(module_size);
+ }
+ }
+ last_end_address = base_address + module_size;
+ }
+
+ modules_ = modules.release();
+ }
+
+ module_count_ = module_count;
+
+ valid_ = true;
+ return true;
+}
+
+
+const MinidumpModule* MinidumpModuleList::GetModuleForAddress(
+ uint64_t address) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress";
+ return NULL;
+ }
+
+ unsigned int module_index;
+ if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
+ NULL /* delta */, NULL /* size */)) {
+ BPLOG(INFO) << "MinidumpModuleList has no module at " <<
+ HexString(address);
+ return NULL;
+ }
+
+ return GetModuleAtIndex(module_index);
+}
+
+
+const MinidumpModule* MinidumpModuleList::GetMainModule() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule";
+ return NULL;
+ }
+
+ // The main code module is the first one present in a minidump file's
+ // MDRawModuleList.
+ return GetModuleAtIndex(0);
+}
+
+
+const MinidumpModule* MinidumpModuleList::GetModuleAtSequence(
+ unsigned int sequence) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence";
+ return NULL;
+ }
+
+ if (sequence >= module_count_) {
+ BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " <<
+ sequence << "/" << module_count_;
+ return NULL;
+ }
+
+ unsigned int module_index;
+ if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
+ NULL /* base */, NULL /* delta */,
+ NULL /* size */)) {
+ BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence;
+ return NULL;
+ }
+
+ return GetModuleAtIndex(module_index);
+}
+
+
+const MinidumpModule* MinidumpModuleList::GetModuleAtIndex(
+ unsigned int index) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex";
+ return NULL;
+ }
+
+ if (index >= module_count_) {
+ BPLOG(ERROR) << "MinidumpModuleList index out of range: " <<
+ index << "/" << module_count_;
+ return NULL;
+ }
+
+ return &(*modules_)[index];
+}
+
+
+const CodeModules* MinidumpModuleList::Copy() const {
+ return new BasicCodeModules(this);
+}
+
+vector<linked_ptr<const CodeModule> >
+MinidumpModuleList::GetShrunkRangeModules() const {
+ return vector<linked_ptr<const CodeModule> >();
+}
+
+bool MinidumpModuleList::IsModuleShrinkEnabled() const {
+ return range_map_->IsShrinkDownEnabled();
+}
+
+void MinidumpModuleList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data";
+ return;
+ }
+
+ printf("MinidumpModuleList\n");
+ printf(" module_count = %d\n", module_count_);
+ printf("\n");
+
+ for (unsigned int module_index = 0;
+ module_index < module_count_;
+ ++module_index) {
+ printf("module[%d]\n", module_index);
+
+ (*modules_)[module_index].Print();
+ }
+}
+
+
+//
+// MinidumpMemoryList
+//
+
+
+uint32_t MinidumpMemoryList::max_regions_ = 4096;
+
+
+MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump)
+ : MinidumpStream(minidump),
+ range_map_(new RangeMap<uint64_t, unsigned int>()),
+ descriptors_(NULL),
+ regions_(NULL),
+ region_count_(0) {
+}
+
+
+MinidumpMemoryList::~MinidumpMemoryList() {
+ delete range_map_;
+ delete descriptors_;
+ delete regions_;
+}
+
+
+bool MinidumpMemoryList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ delete descriptors_;
+ descriptors_ = NULL;
+ delete regions_;
+ regions_ = NULL;
+ range_map_->Clear();
+ region_count_ = 0;
+
+ valid_ = false;
+
+ uint32_t region_count;
+ if (expected_size < sizeof(region_count)) {
+ BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " <<
+ expected_size << " < " << sizeof(region_count);
+ return false;
+ }
+ if (!minidump_->ReadBytes(&region_count, sizeof(region_count))) {
+ BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count";
+ return false;
+ }
+
+ if (minidump_->swap())
+ Swap(&region_count);
+
+ if (region_count >
+ numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) {
+ BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count <<
+ " would cause multiplication overflow";
+ return false;
+ }
+
+ if (expected_size != sizeof(region_count) +
+ region_count * sizeof(MDMemoryDescriptor)) {
+ // may be padded with 4 bytes on 64bit ABIs for alignment
+ if (expected_size == sizeof(region_count) + 4 +
+ region_count * sizeof(MDMemoryDescriptor)) {
+ uint32_t useless;
+ if (!minidump_->ReadBytes(&useless, 4)) {
+ BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded "
+ "bytes";
+ return false;
+ }
+ } else {
+ BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size <<
+ " != " << sizeof(region_count) +
+ region_count * sizeof(MDMemoryDescriptor);
+ return false;
+ }
+ }
+
+ if (region_count > max_regions_) {
+ BPLOG(ERROR) << "MinidumpMemoryList count " << region_count <<
+ " exceeds maximum " << max_regions_;
+ return false;
+ }
+
+ if (region_count != 0) {
+ scoped_ptr<MemoryDescriptors> descriptors(
+ new MemoryDescriptors(region_count));
+
+ // Read the entire array in one fell swoop, instead of reading one entry
+ // at a time in the loop.
+ if (!minidump_->ReadBytes(&(*descriptors)[0],
+ sizeof(MDMemoryDescriptor) * region_count)) {
+ BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list";
+ return false;
+ }
+
+ scoped_ptr<MemoryRegions> regions(
+ new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_)));
+
+ for (unsigned int region_index = 0;
+ region_index < region_count;
+ ++region_index) {
+ MDMemoryDescriptor* descriptor = &(*descriptors)[region_index];
+
+ if (minidump_->swap())
+ Swap(descriptor);
+
+ uint64_t base_address = descriptor->start_of_memory_range;
+ uint32_t region_size = descriptor->memory.data_size;
+
+ // Check for base + size overflow or undersize.
+ if (region_size == 0 ||
+ region_size > numeric_limits<uint64_t>::max() - base_address) {
+ BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " <<
+ " region " << region_index << "/" << region_count <<
+ ", " << HexString(base_address) << "+" <<
+ HexString(region_size);
+ return false;
+ }
+
+ if (!range_map_->StoreRange(base_address, region_size, region_index)) {
+ BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " <<
+ region_index << "/" << region_count << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(region_size);
+ return false;
+ }
+
+ (*regions)[region_index].SetDescriptor(descriptor);
+ }
+
+ descriptors_ = descriptors.release();
+ regions_ = regions.release();
+ }
+
+ region_count_ = region_count;
+
+ valid_ = true;
+ return true;
+}
+
+
+MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex(
+ unsigned int index) {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex";
+ return NULL;
+ }
+
+ if (index >= region_count_) {
+ BPLOG(ERROR) << "MinidumpMemoryList index out of range: " <<
+ index << "/" << region_count_;
+ return NULL;
+ }
+
+ return &(*regions_)[index];
+}
+
+
+MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress(
+ uint64_t address) {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress";
+ return NULL;
+ }
+
+ unsigned int region_index;
+ if (!range_map_->RetrieveRange(address, &region_index, NULL /* base */,
+ NULL /* delta */, NULL /* size */)) {
+ BPLOG(INFO) << "MinidumpMemoryList has no memory region at " <<
+ HexString(address);
+ return NULL;
+ }
+
+ return GetMemoryRegionAtIndex(region_index);
+}
+
+
+void MinidumpMemoryList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data";
+ return;
+ }
+
+ printf("MinidumpMemoryList\n");
+ printf(" region_count = %d\n", region_count_);
+ printf("\n");
+
+ for (unsigned int region_index = 0;
+ region_index < region_count_;
+ ++region_index) {
+ MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index];
+ printf("region[%d]\n", region_index);
+ printf("MDMemoryDescriptor\n");
+ printf(" start_of_memory_range = 0x%" PRIx64 "\n",
+ descriptor->start_of_memory_range);
+ printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size);
+ printf(" memory.rva = 0x%x\n", descriptor->memory.rva);
+ MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index);
+ if (region) {
+ printf("Memory\n");
+ region->Print();
+ } else {
+ printf("No memory\n");
+ }
+ printf("\n");
+ }
+}
+
+
+//
+// MinidumpException
+//
+
+
+MinidumpException::MinidumpException(Minidump* minidump)
+ : MinidumpStream(minidump),
+ exception_(),
+ context_(NULL) {
+}
+
+
+MinidumpException::~MinidumpException() {
+ delete context_;
+}
+
+
+bool MinidumpException::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ delete context_;
+ context_ = NULL;
+
+ valid_ = false;
+
+ if (expected_size != sizeof(exception_)) {
+ BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size <<
+ " != " << sizeof(exception_);
+ return false;
+ }
+
+ if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) {
+ BPLOG(ERROR) << "MinidumpException cannot read exception";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&exception_.thread_id);
+ // exception_.__align is for alignment only and does not need to be
+ // swapped.
+ Swap(&exception_.exception_record.exception_code);
+ Swap(&exception_.exception_record.exception_flags);
+ Swap(&exception_.exception_record.exception_record);
+ Swap(&exception_.exception_record.exception_address);
+ Swap(&exception_.exception_record.number_parameters);
+ // exception_.exception_record.__align is for alignment only and does not
+ // need to be swapped.
+ for (unsigned int parameter_index = 0;
+ parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS;
+ ++parameter_index) {
+ Swap(&exception_.exception_record.exception_information[parameter_index]);
+ }
+ Swap(&exception_.thread_context);
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+bool MinidumpException::GetThreadID(uint32_t *thread_id) const {
+ BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires "
+ "|thread_id|";
+ assert(thread_id);
+ *thread_id = 0;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID";
+ return false;
+ }
+
+ *thread_id = exception_.thread_id;
+ return true;
+}
+
+
+MinidumpContext* MinidumpException::GetContext() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpException for GetContext";
+ return NULL;
+ }
+
+ if (!context_) {
+ if (!minidump_->SeekSet(exception_.thread_context.rva)) {
+ BPLOG(ERROR) << "MinidumpException cannot seek to context";
+ return NULL;
+ }
+
+ scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
+
+ // Don't log as an error if we can still fall back on the thread's context
+ // (which must be possible if we got this far.)
+ if (!context->Read(exception_.thread_context.data_size)) {
+ BPLOG(INFO) << "MinidumpException cannot read context";
+ return NULL;
+ }
+
+ context_ = context.release();
+ }
+
+ return context_;
+}
+
+
+void MinidumpException::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpException cannot print invalid data";
+ return;
+ }
+
+ printf("MDException\n");
+ printf(" thread_id = 0x%x\n",
+ exception_.thread_id);
+ printf(" exception_record.exception_code = 0x%x\n",
+ exception_.exception_record.exception_code);
+ printf(" exception_record.exception_flags = 0x%x\n",
+ exception_.exception_record.exception_flags);
+ printf(" exception_record.exception_record = 0x%" PRIx64 "\n",
+ exception_.exception_record.exception_record);
+ printf(" exception_record.exception_address = 0x%" PRIx64 "\n",
+ exception_.exception_record.exception_address);
+ printf(" exception_record.number_parameters = %d\n",
+ exception_.exception_record.number_parameters);
+ for (unsigned int parameterIndex = 0;
+ parameterIndex < exception_.exception_record.number_parameters;
+ ++parameterIndex) {
+ printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n",
+ parameterIndex,
+ exception_.exception_record.exception_information[parameterIndex]);
+ }
+ printf(" thread_context.data_size = %d\n",
+ exception_.thread_context.data_size);
+ printf(" thread_context.rva = 0x%x\n",
+ exception_.thread_context.rva);
+ MinidumpContext* context = GetContext();
+ if (context) {
+ printf("\n");
+ context->Print();
+ } else {
+ printf(" (no context)\n");
+ printf("\n");
+ }
+}
+
+//
+// MinidumpAssertion
+//
+
+
+MinidumpAssertion::MinidumpAssertion(Minidump* minidump)
+ : MinidumpStream(minidump),
+ assertion_(),
+ expression_(),
+ function_(),
+ file_() {
+}
+
+
+MinidumpAssertion::~MinidumpAssertion() {
+}
+
+
+bool MinidumpAssertion::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ valid_ = false;
+
+ if (expected_size != sizeof(assertion_)) {
+ BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size <<
+ " != " << sizeof(assertion_);
+ return false;
+ }
+
+ if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) {
+ BPLOG(ERROR) << "MinidumpAssertion cannot read assertion";
+ return false;
+ }
+
+ // Each of {expression, function, file} is a UTF-16 string,
+ // we'll convert them to UTF-8 for ease of use.
+ ConvertUTF16BufferToUTF8String(assertion_.expression,
+ sizeof(assertion_.expression), &expression_,
+ minidump_->swap());
+ ConvertUTF16BufferToUTF8String(assertion_.function,
+ sizeof(assertion_.function), &function_,
+ minidump_->swap());
+ ConvertUTF16BufferToUTF8String(assertion_.file, sizeof(assertion_.file),
+ &file_, minidump_->swap());
+
+ if (minidump_->swap()) {
+ Swap(&assertion_.line);
+ Swap(&assertion_.type);
+ }
+
+ valid_ = true;
+ return true;
+}
+
+void MinidumpAssertion::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data";
+ return;
+ }
+
+ printf("MDAssertion\n");
+ printf(" expression = %s\n",
+ expression_.c_str());
+ printf(" function = %s\n",
+ function_.c_str());
+ printf(" file = %s\n",
+ file_.c_str());
+ printf(" line = %u\n",
+ assertion_.line);
+ printf(" type = %u\n",
+ assertion_.type);
+ printf("\n");
+}
+
+//
+// MinidumpSystemInfo
+//
+
+
+MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump)
+ : MinidumpStream(minidump),
+ system_info_(),
+ csd_version_(NULL),
+ cpu_vendor_(NULL) {
+}
+
+
+MinidumpSystemInfo::~MinidumpSystemInfo() {
+ delete csd_version_;
+ delete cpu_vendor_;
+}
+
+
+bool MinidumpSystemInfo::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ delete csd_version_;
+ csd_version_ = NULL;
+ delete cpu_vendor_;
+ cpu_vendor_ = NULL;
+
+ valid_ = false;
+
+ if (expected_size != sizeof(system_info_)) {
+ BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size <<
+ " != " << sizeof(system_info_);
+ return false;
+ }
+
+ if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) {
+ BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&system_info_.processor_architecture);
+ Swap(&system_info_.processor_level);
+ Swap(&system_info_.processor_revision);
+ // number_of_processors and product_type are 8-bit quantities and need no
+ // swapping.
+ Swap(&system_info_.major_version);
+ Swap(&system_info_.minor_version);
+ Swap(&system_info_.build_number);
+ Swap(&system_info_.platform_id);
+ Swap(&system_info_.csd_version_rva);
+ Swap(&system_info_.suite_mask);
+ // Don't swap the reserved2 field because its contents are unknown.
+
+ if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
+ system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
+ for (unsigned int i = 0; i < 3; ++i)
+ Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]);
+ Swap(&system_info_.cpu.x86_cpu_info.version_information);
+ Swap(&system_info_.cpu.x86_cpu_info.feature_information);
+ Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
+ } else {
+ for (unsigned int i = 0; i < 2; ++i)
+ Swap(&system_info_.cpu.other_cpu_info.processor_features[i]);
+ }
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+string MinidumpSystemInfo::GetOS() {
+ string os;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS";
+ return os;
+ }
+
+ switch (system_info_.platform_id) {
+ case MD_OS_WIN32_NT:
+ case MD_OS_WIN32_WINDOWS:
+ os = "windows";
+ break;
+
+ case MD_OS_MAC_OS_X:
+ os = "mac";
+ break;
+
+ case MD_OS_IOS:
+ os = "ios";
+ break;
+
+ case MD_OS_LINUX:
+ os = "linux";
+ break;
+
+ case MD_OS_SOLARIS:
+ os = "solaris";
+ break;
+
+ case MD_OS_ANDROID:
+ os = "android";
+ break;
+
+ case MD_OS_PS3:
+ os = "ps3";
+ break;
+
+ case MD_OS_NACL:
+ os = "nacl";
+ break;
+
+ default:
+ BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " <<
+ HexString(system_info_.platform_id);
+ break;
+ }
+
+ return os;
+}
+
+
+string MinidumpSystemInfo::GetCPU() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU";
+ return "";
+ }
+
+ string cpu;
+
+ switch (system_info_.processor_architecture) {
+ case MD_CPU_ARCHITECTURE_X86:
+ case MD_CPU_ARCHITECTURE_X86_WIN64:
+ cpu = "x86";
+ break;
+
+ case MD_CPU_ARCHITECTURE_AMD64:
+ cpu = "x86-64";
+ break;
+
+ case MD_CPU_ARCHITECTURE_PPC:
+ cpu = "ppc";
+ break;
+
+ case MD_CPU_ARCHITECTURE_PPC64:
+ cpu = "ppc64";
+ break;
+
+ case MD_CPU_ARCHITECTURE_SPARC:
+ cpu = "sparc";
+ break;
+
+ case MD_CPU_ARCHITECTURE_ARM:
+ cpu = "arm";
+ break;
+
+ case MD_CPU_ARCHITECTURE_ARM64:
+ cpu = "arm64";
+ break;
+
+ default:
+ BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
+ HexString(system_info_.processor_architecture);
+ break;
+ }
+
+ return cpu;
+}
+
+
+const string* MinidumpSystemInfo::GetCSDVersion() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion";
+ return NULL;
+ }
+
+ if (!csd_version_)
+ csd_version_ = minidump_->ReadString(system_info_.csd_version_rva);
+
+ BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read "
+ "CSD version";
+
+ return csd_version_;
+}
+
+
+const string* MinidumpSystemInfo::GetCPUVendor() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor";
+ return NULL;
+ }
+
+ // CPU vendor information can only be determined from x86 minidumps.
+ if (!cpu_vendor_ &&
+ (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
+ system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) {
+ char cpu_vendor_string[13];
+ snprintf(cpu_vendor_string, sizeof(cpu_vendor_string),
+ "%c%c%c%c%c%c%c%c%c%c%c%c",
+ system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff,
+ system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff,
+ system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff,
+ (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff);
+ cpu_vendor_ = new string(cpu_vendor_string);
+ }
+
+ return cpu_vendor_;
+}
+
+
+void MinidumpSystemInfo::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawSystemInfo\n");
+ printf(" processor_architecture = 0x%x\n",
+ system_info_.processor_architecture);
+ printf(" processor_level = %d\n",
+ system_info_.processor_level);
+ printf(" processor_revision = 0x%x\n",
+ system_info_.processor_revision);
+ printf(" number_of_processors = %d\n",
+ system_info_.number_of_processors);
+ printf(" product_type = %d\n",
+ system_info_.product_type);
+ printf(" major_version = %d\n",
+ system_info_.major_version);
+ printf(" minor_version = %d\n",
+ system_info_.minor_version);
+ printf(" build_number = %d\n",
+ system_info_.build_number);
+ printf(" platform_id = 0x%x\n",
+ system_info_.platform_id);
+ printf(" csd_version_rva = 0x%x\n",
+ system_info_.csd_version_rva);
+ printf(" suite_mask = 0x%x\n",
+ system_info_.suite_mask);
+ if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
+ system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
+ printf(" cpu.x86_cpu_info (valid):\n");
+ } else {
+ printf(" cpu.x86_cpu_info (invalid):\n");
+ }
+ for (unsigned int i = 0; i < 3; ++i) {
+ printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n",
+ i, system_info_.cpu.x86_cpu_info.vendor_id[i]);
+ }
+ printf(" cpu.x86_cpu_info.version_information = 0x%x\n",
+ system_info_.cpu.x86_cpu_info.version_information);
+ printf(" cpu.x86_cpu_info.feature_information = 0x%x\n",
+ system_info_.cpu.x86_cpu_info.feature_information);
+ printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n",
+ system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
+ if (system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86 &&
+ system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86_WIN64) {
+ printf(" cpu.other_cpu_info (valid):\n");
+ for (unsigned int i = 0; i < 2; ++i) {
+ printf(" cpu.other_cpu_info.processor_features[%d] = 0x%" PRIx64 "\n",
+ i, system_info_.cpu.other_cpu_info.processor_features[i]);
+ }
+ }
+ const string* csd_version = GetCSDVersion();
+ if (csd_version) {
+ printf(" (csd_version) = \"%s\"\n",
+ csd_version->c_str());
+ } else {
+ printf(" (csd_version) = (null)\n");
+ }
+ const string* cpu_vendor = GetCPUVendor();
+ if (cpu_vendor) {
+ printf(" (cpu_vendor) = \"%s\"\n",
+ cpu_vendor->c_str());
+ } else {
+ printf(" (cpu_vendor) = (null)\n");
+ }
+ printf("\n");
+}
+
+
+//
+// MinidumpMiscInfo
+//
+
+
+MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump)
+ : MinidumpStream(minidump),
+ misc_info_() {
+}
+
+
+bool MinidumpMiscInfo::Read(uint32_t expected_size) {
+ valid_ = false;
+
+ size_t padding = 0;
+ if (expected_size != MD_MISCINFO_SIZE &&
+ expected_size != MD_MISCINFO2_SIZE &&
+ expected_size != MD_MISCINFO3_SIZE &&
+ expected_size != MD_MISCINFO4_SIZE &&
+ expected_size != MD_MISCINFO5_SIZE) {
+ if (expected_size > MD_MISCINFO5_SIZE) {
+ // Only read the part of the misc info structure we know how to handle
+ BPLOG(INFO) << "MinidumpMiscInfo size larger than expected "
+ << expected_size << ", skipping over the unknown part";
+ padding = expected_size - MD_MISCINFO5_SIZE;
+ expected_size = MD_MISCINFO5_SIZE;
+ } else {
+ BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size
+ << " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE
+ << ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE
+ << ", " << MD_MISCINFO5_SIZE << ")";
+ return false;
+ }
+ }
+
+ if (!minidump_->ReadBytes(&misc_info_, expected_size)) {
+ BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info";
+ return false;
+ }
+
+ if (padding != 0) {
+ off_t saved_position = minidump_->Tell();
+ if (saved_position == -1) {
+ BPLOG(ERROR) << "MinidumpMiscInfo could not tell the current position";
+ return false;
+ }
+
+ if (!minidump_->SeekSet(saved_position + padding)) {
+ BPLOG(ERROR) << "MinidumpMiscInfo could not seek past the miscellaneous "
+ << "info structure";
+ return false;
+ }
+ }
+
+ if (minidump_->swap()) {
+ // Swap version 1 fields
+ Swap(&misc_info_.size_of_info);
+ Swap(&misc_info_.flags1);
+ Swap(&misc_info_.process_id);
+ Swap(&misc_info_.process_create_time);
+ Swap(&misc_info_.process_user_time);
+ Swap(&misc_info_.process_kernel_time);
+ if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
+ // Swap version 2 fields
+ Swap(&misc_info_.processor_max_mhz);
+ Swap(&misc_info_.processor_current_mhz);
+ Swap(&misc_info_.processor_mhz_limit);
+ Swap(&misc_info_.processor_max_idle_state);
+ Swap(&misc_info_.processor_current_idle_state);
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
+ // Swap version 3 fields
+ Swap(&misc_info_.process_integrity_level);
+ Swap(&misc_info_.process_execute_flags);
+ Swap(&misc_info_.protected_process);
+ Swap(&misc_info_.time_zone_id);
+ Swap(&misc_info_.time_zone);
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
+ // Swap version 4 fields.
+ // Do not swap UTF-16 strings. The swap is done as part of the
+ // conversion to UTF-8 (code follows below).
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
+ // Swap version 5 fields
+ Swap(&misc_info_.xstate_data);
+ Swap(&misc_info_.process_cookie);
+ }
+ }
+
+ if (expected_size + padding != misc_info_.size_of_info) {
+ BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " <<
+ expected_size << " != " << misc_info_.size_of_info;
+ return false;
+ }
+
+ // Convert UTF-16 strings
+ if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
+ // Convert UTF-16 strings in version 3 fields
+ ConvertUTF16BufferToUTF8String(misc_info_.time_zone.standard_name,
+ sizeof(misc_info_.time_zone.standard_name),
+ &standard_name_, minidump_->swap());
+ ConvertUTF16BufferToUTF8String(misc_info_.time_zone.daylight_name,
+ sizeof(misc_info_.time_zone.daylight_name),
+ &daylight_name_, minidump_->swap());
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
+ // Convert UTF-16 strings in version 4 fields
+ ConvertUTF16BufferToUTF8String(misc_info_.build_string,
+ sizeof(misc_info_.build_string),
+ &build_string_, minidump_->swap());
+ ConvertUTF16BufferToUTF8String(misc_info_.dbg_bld_str,
+ sizeof(misc_info_.dbg_bld_str),
+ &dbg_bld_str_, minidump_->swap());
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+void MinidumpMiscInfo::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawMiscInfo\n");
+ // Print version 1 fields
+ printf(" size_of_info = %d\n", misc_info_.size_of_info);
+ printf(" flags1 = 0x%x\n", misc_info_.flags1);
+ printf(" process_id = ");
+ PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_ID,
+ kNumberFormatDecimal, misc_info_.process_id);
+ if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES) {
+ printf(" process_create_time = 0x%x %s\n",
+ misc_info_.process_create_time,
+ TimeTToUTCString(misc_info_.process_create_time).c_str());
+ } else {
+ printf(" process_create_time = (invalid)\n");
+ }
+ printf(" process_user_time = ");
+ PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
+ kNumberFormatDecimal, misc_info_.process_user_time);
+ printf(" process_kernel_time = ");
+ PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
+ kNumberFormatDecimal, misc_info_.process_kernel_time);
+ if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
+ // Print version 2 fields
+ printf(" processor_max_mhz = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
+ kNumberFormatDecimal, misc_info_.processor_max_mhz);
+ printf(" processor_current_mhz = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
+ kNumberFormatDecimal, misc_info_.processor_current_mhz);
+ printf(" processor_mhz_limit = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
+ kNumberFormatDecimal, misc_info_.processor_mhz_limit);
+ printf(" processor_max_idle_state = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
+ kNumberFormatDecimal,
+ misc_info_.processor_max_idle_state);
+ printf(" processor_current_idle_state = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
+ kNumberFormatDecimal,
+ misc_info_.processor_current_idle_state);
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
+ // Print version 3 fields
+ printf(" process_integrity_level = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY,
+ kNumberFormatHexadecimal,
+ misc_info_.process_integrity_level);
+ printf(" process_execute_flags = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS,
+ kNumberFormatHexadecimal,
+ misc_info_.process_execute_flags);
+ printf(" protected_process = ");
+ PrintValueOrInvalid(misc_info_.flags1 &
+ MD_MISCINFO_FLAGS1_PROTECTED_PROCESS,
+ kNumberFormatDecimal, misc_info_.protected_process);
+ printf(" time_zone_id = ");
+ PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE,
+ kNumberFormatDecimal, misc_info_.time_zone_id);
+ if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE) {
+ printf(" time_zone.bias = %d\n",
+ misc_info_.time_zone.bias);
+ printf(" time_zone.standard_name = %s\n", standard_name_.c_str());
+ printf(" time_zone.standard_date = "
+ "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
+ misc_info_.time_zone.standard_date.year,
+ misc_info_.time_zone.standard_date.month,
+ misc_info_.time_zone.standard_date.day,
+ misc_info_.time_zone.standard_date.day_of_week,
+ misc_info_.time_zone.standard_date.hour,
+ misc_info_.time_zone.standard_date.minute,
+ misc_info_.time_zone.standard_date.second,
+ misc_info_.time_zone.standard_date.milliseconds);
+ printf(" time_zone.standard_bias = %d\n",
+ misc_info_.time_zone.standard_bias);
+ printf(" time_zone.daylight_name = %s\n", daylight_name_.c_str());
+ printf(" time_zone.daylight_date = "
+ "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
+ misc_info_.time_zone.daylight_date.year,
+ misc_info_.time_zone.daylight_date.month,
+ misc_info_.time_zone.daylight_date.day,
+ misc_info_.time_zone.daylight_date.day_of_week,
+ misc_info_.time_zone.daylight_date.hour,
+ misc_info_.time_zone.daylight_date.minute,
+ misc_info_.time_zone.daylight_date.second,
+ misc_info_.time_zone.daylight_date.milliseconds);
+ printf(" time_zone.daylight_bias = %d\n",
+ misc_info_.time_zone.daylight_bias);
+ } else {
+ printf(" time_zone.bias = (invalid)\n");
+ printf(" time_zone.standard_name = (invalid)\n");
+ printf(" time_zone.standard_date = (invalid)\n");
+ printf(" time_zone.standard_bias = (invalid)\n");
+ printf(" time_zone.daylight_name = (invalid)\n");
+ printf(" time_zone.daylight_date = (invalid)\n");
+ printf(" time_zone.daylight_bias = (invalid)\n");
+ }
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
+ // Print version 4 fields
+ if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_BUILDSTRING) {
+ printf(" build_string = %s\n", build_string_.c_str());
+ printf(" dbg_bld_str = %s\n", dbg_bld_str_.c_str());
+ } else {
+ printf(" build_string = (invalid)\n");
+ printf(" dbg_bld_str = (invalid)\n");
+ }
+ }
+ if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
+ // Print version 5 fields
+ if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_COOKIE) {
+ printf(" xstate_data.size_of_info = %d\n",
+ misc_info_.xstate_data.size_of_info);
+ printf(" xstate_data.context_size = %d\n",
+ misc_info_.xstate_data.context_size);
+ printf(" xstate_data.enabled_features = 0x%" PRIx64 "\n",
+ misc_info_.xstate_data.enabled_features);
+ for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
+ if (misc_info_.xstate_data.enabled_features & (1 << i)) {
+ printf(" xstate_data.features[%02zu] = { %d, %d }\n", i,
+ misc_info_.xstate_data.features[i].offset,
+ misc_info_.xstate_data.features[i].size);
+ }
+ }
+ if (misc_info_.xstate_data.enabled_features == 0) {
+ printf(" xstate_data.features[] = (empty)\n");
+ }
+ printf(" process_cookie = %d\n",
+ misc_info_.process_cookie);
+ } else {
+ printf(" xstate_data.size_of_info = (invalid)\n");
+ printf(" xstate_data.context_size = (invalid)\n");
+ printf(" xstate_data.enabled_features = (invalid)\n");
+ printf(" xstate_data.features[] = (invalid)\n");
+ printf(" process_cookie = (invalid)\n");
+ }
+ }
+ printf("\n");
+}
+
+
+//
+// MinidumpBreakpadInfo
+//
+
+
+MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump)
+ : MinidumpStream(minidump),
+ breakpad_info_() {
+}
+
+
+bool MinidumpBreakpadInfo::Read(uint32_t expected_size) {
+ valid_ = false;
+
+ if (expected_size != sizeof(breakpad_info_)) {
+ BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size <<
+ " != " << sizeof(breakpad_info_);
+ return false;
+ }
+
+ if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) {
+ BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&breakpad_info_.validity);
+ Swap(&breakpad_info_.dump_thread_id);
+ Swap(&breakpad_info_.requesting_thread_id);
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t *thread_id) const {
+ BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID "
+ "requires |thread_id|";
+ assert(thread_id);
+ *thread_id = 0;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID";
+ return false;
+ }
+
+ if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) {
+ BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread";
+ return false;
+ }
+
+ *thread_id = breakpad_info_.dump_thread_id;
+ return true;
+}
+
+
+bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t *thread_id)
+ const {
+ BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID "
+ "requires |thread_id|";
+ assert(thread_id);
+ *thread_id = 0;
+
+ if (!thread_id || !valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID";
+ return false;
+ }
+
+ if (!(breakpad_info_.validity &
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) {
+ BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread";
+ return false;
+ }
+
+ *thread_id = breakpad_info_.requesting_thread_id;
+ return true;
+}
+
+
+void MinidumpBreakpadInfo::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawBreakpadInfo\n");
+ printf(" validity = 0x%x\n", breakpad_info_.validity);
+ printf(" dump_thread_id = ");
+ PrintValueOrInvalid(breakpad_info_.validity &
+ MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID,
+ kNumberFormatHexadecimal, breakpad_info_.dump_thread_id);
+ printf(" requesting_thread_id = ");
+ PrintValueOrInvalid(breakpad_info_.validity &
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID,
+ kNumberFormatHexadecimal,
+ breakpad_info_.requesting_thread_id);
+
+ printf("\n");
+}
+
+
+//
+// MinidumpMemoryInfo
+//
+
+
+MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump)
+ : MinidumpObject(minidump),
+ memory_info_() {
+}
+
+
+bool MinidumpMemoryInfo::IsExecutable() const {
+ uint32_t protection =
+ memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
+ return protection == MD_MEMORY_PROTECT_EXECUTE ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READ ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE;
+}
+
+
+bool MinidumpMemoryInfo::IsWritable() const {
+ uint32_t protection =
+ memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
+ return protection == MD_MEMORY_PROTECT_READWRITE ||
+ protection == MD_MEMORY_PROTECT_WRITECOPY ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY;
+}
+
+
+bool MinidumpMemoryInfo::Read() {
+ valid_ = false;
+
+ if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&memory_info_.base_address);
+ Swap(&memory_info_.allocation_base);
+ Swap(&memory_info_.allocation_protection);
+ Swap(&memory_info_.region_size);
+ Swap(&memory_info_.state);
+ Swap(&memory_info_.protection);
+ Swap(&memory_info_.type);
+ }
+
+ // Check for base + size overflow or undersize.
+ if (memory_info_.region_size == 0 ||
+ memory_info_.region_size > numeric_limits<uint64_t>::max() -
+ memory_info_.base_address) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " <<
+ HexString(memory_info_.base_address) << "+" <<
+ HexString(memory_info_.region_size);
+ return false;
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+void MinidumpMemoryInfo::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawMemoryInfo\n");
+ printf(" base_address = 0x%" PRIx64 "\n",
+ memory_info_.base_address);
+ printf(" allocation_base = 0x%" PRIx64 "\n",
+ memory_info_.allocation_base);
+ printf(" allocation_protection = 0x%x\n",
+ memory_info_.allocation_protection);
+ printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size);
+ printf(" state = 0x%x\n", memory_info_.state);
+ printf(" protection = 0x%x\n", memory_info_.protection);
+ printf(" type = 0x%x\n", memory_info_.type);
+}
+
+
+//
+// MinidumpMemoryInfoList
+//
+
+
+MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump)
+ : MinidumpStream(minidump),
+ range_map_(new RangeMap<uint64_t, unsigned int>()),
+ infos_(NULL),
+ info_count_(0) {
+}
+
+
+MinidumpMemoryInfoList::~MinidumpMemoryInfoList() {
+ delete range_map_;
+ delete infos_;
+}
+
+
+bool MinidumpMemoryInfoList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ delete infos_;
+ infos_ = NULL;
+ range_map_->Clear();
+ info_count_ = 0;
+
+ valid_ = false;
+
+ MDRawMemoryInfoList header;
+ if (expected_size < sizeof(MDRawMemoryInfoList)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
+ expected_size << " < " << sizeof(MDRawMemoryInfoList);
+ return false;
+ }
+ if (!minidump_->ReadBytes(&header, sizeof(header))) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&header.size_of_header);
+ Swap(&header.size_of_entry);
+ Swap(&header.number_of_entries);
+ }
+
+ // Sanity check that the header is the expected size.
+ // TODO(ted): could possibly handle this more gracefully, assuming
+ // that future versions of the structs would be backwards-compatible.
+ if (header.size_of_header != sizeof(MDRawMemoryInfoList)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
+ header.size_of_header << " != " <<
+ sizeof(MDRawMemoryInfoList);
+ return false;
+ }
+
+ // Sanity check that the entries are the expected size.
+ if (header.size_of_entry != sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " <<
+ header.size_of_entry << " != " <<
+ sizeof(MDRawMemoryInfo);
+ return false;
+ }
+
+ if (header.number_of_entries >
+ numeric_limits<uint32_t>::max() / sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList info count " <<
+ header.number_of_entries <<
+ " would cause multiplication overflow";
+ return false;
+ }
+
+ if (expected_size != sizeof(MDRawMemoryInfoList) +
+ header.number_of_entries * sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size <<
+ " != " << sizeof(MDRawMemoryInfoList) +
+ header.number_of_entries * sizeof(MDRawMemoryInfo);
+ return false;
+ }
+
+ // Check for data loss when converting header.number_of_entries from
+ // uint64_t into MinidumpMemoryInfos::size_type (uint32_t)
+ MinidumpMemoryInfos::size_type header_number_of_entries =
+ static_cast<unsigned int>(header.number_of_entries);
+ if (static_cast<uint64_t>(header_number_of_entries) !=
+ header.number_of_entries) {
+ BPLOG(ERROR) << "Data loss detected when converting "
+ "the header's number_of_entries";
+ return false;
+ }
+
+ if (header.number_of_entries != 0) {
+ scoped_ptr<MinidumpMemoryInfos> infos(
+ new MinidumpMemoryInfos(header_number_of_entries,
+ MinidumpMemoryInfo(minidump_)));
+
+ for (unsigned int index = 0;
+ index < header.number_of_entries;
+ ++index) {
+ MinidumpMemoryInfo* info = &(*infos)[index];
+
+ // Assume that the file offset is correct after the last read.
+ if (!info->Read()) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " <<
+ index << "/" << header.number_of_entries;
+ return false;
+ }
+
+ uint64_t base_address = info->GetBase();
+ uint64_t region_size = info->GetSize();
+
+ if (!range_map_->StoreRange(base_address, region_size, index)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList could not store"
+ " memory region " <<
+ index << "/" << header.number_of_entries << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(region_size);
+ return false;
+ }
+ }
+
+ infos_ = infos.release();
+ }
+
+ info_count_ = header_number_of_entries;
+
+ valid_ = true;
+ return true;
+}
+
+
+const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex(
+ unsigned int index) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex";
+ return NULL;
+ }
+
+ if (index >= info_count_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " <<
+ index << "/" << info_count_;
+ return NULL;
+ }
+
+ return &(*infos_)[index];
+}
+
+
+const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress(
+ uint64_t address) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for"
+ " GetMemoryInfoForAddress";
+ return NULL;
+ }
+
+ unsigned int info_index;
+ if (!range_map_->RetrieveRange(address, &info_index, NULL /* base */,
+ NULL /* delta */, NULL /* size */)) {
+ BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " <<
+ HexString(address);
+ return NULL;
+ }
+
+ return GetMemoryInfoAtIndex(info_index);
+}
+
+
+void MinidumpMemoryInfoList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data";
+ return;
+ }
+
+ printf("MinidumpMemoryInfoList\n");
+ printf(" info_count = %d\n", info_count_);
+ printf("\n");
+
+ for (unsigned int info_index = 0;
+ info_index < info_count_;
+ ++info_index) {
+ printf("info[%d]\n", info_index);
+ (*infos_)[info_index].Print();
+ printf("\n");
+ }
+}
+
+//
+// MinidumpLinuxMaps
+//
+
+MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump *minidump)
+ : MinidumpObject(minidump) {
+}
+
+void MinidumpLinuxMaps::Print() const {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpLinuxMaps cannot print invalid data";
+ return;
+ }
+ std::cout << region_.line << std::endl;
+}
+
+//
+// MinidumpLinuxMapsList
+//
+
+MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump *minidump)
+ : MinidumpStream(minidump),
+ maps_(NULL),
+ maps_count_(0) {
+}
+
+MinidumpLinuxMapsList::~MinidumpLinuxMapsList() {
+ if (maps_) {
+ for (unsigned int i = 0; i < maps_->size(); i++) {
+ delete (*maps_)[i];
+ }
+ delete maps_;
+ }
+}
+
+const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsForAddress(
+ uint64_t address) const {
+ if (!valid_ || (maps_ == NULL)) {
+ BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsForAddress";
+ return NULL;
+ }
+
+ // Search every memory mapping.
+ for (unsigned int index = 0; index < maps_count_; index++) {
+ // Check if address is within bounds of the current memory region.
+ if ((*maps_)[index]->GetBase() <= address &&
+ (*maps_)[index]->GetBase() + (*maps_)[index]->GetSize() > address) {
+ return (*maps_)[index];
+ }
+ }
+
+ // No mapping encloses the memory address.
+ BPLOG(ERROR) << "MinidumpLinuxMapsList has no mapping at "
+ << HexString(address);
+ return NULL;
+}
+
+const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsAtIndex(
+ unsigned int index) const {
+ if (!valid_ || (maps_ == NULL)) {
+ BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsAtIndex";
+ return NULL;
+ }
+
+ // Index out of bounds.
+ if (index >= maps_count_ || (maps_ == NULL)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList index of out range: "
+ << index
+ << "/"
+ << maps_count_;
+ return NULL;
+ }
+ return (*maps_)[index];
+}
+
+bool MinidumpLinuxMapsList::Read(uint32_t expected_size) {
+ // Invalidate cached data.
+ if (maps_) {
+ for (unsigned int i = 0; i < maps_->size(); i++) {
+ delete (*maps_)[i];
+ }
+ delete maps_;
+ }
+ maps_ = NULL;
+ maps_count_ = 0;
+
+ valid_ = false;
+
+ // Load and check expected stream length.
+ uint32_t length = 0;
+ if (!minidump_->SeekToStreamType(MD_LINUX_MAPS, &length)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList stream type not found";
+ return false;
+ }
+ if (expected_size != length) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList size mismatch: "
+ << expected_size
+ << " != "
+ << length;
+ return false;
+ }
+
+ // Create a vector to read stream data. The vector needs to have
+ // at least enough capacity to read all the data.
+ vector<char> mapping_bytes(length);
+ if (!minidump_->ReadBytes(&mapping_bytes[0], length)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList failed to read bytes";
+ return false;
+ }
+ string map_string(mapping_bytes.begin(), mapping_bytes.end());
+ vector<MappedMemoryRegion> all_regions;
+
+ // Parse string into mapping data.
+ if (!ParseProcMaps(map_string, &all_regions)) {
+ return false;
+ }
+
+ scoped_ptr<MinidumpLinuxMappings> maps(new MinidumpLinuxMappings());
+
+ // Push mapping data into wrapper classes.
+ for (size_t i = 0; i < all_regions.size(); i++) {
+ scoped_ptr<MinidumpLinuxMaps> ele(new MinidumpLinuxMaps(minidump_));
+ ele->region_ = all_regions[i];
+ ele->valid_ = true;
+ maps->push_back(ele.release());
+ }
+
+ // Set instance variables.
+ maps_ = maps.release();
+ maps_count_ = maps_->size();
+ valid_ = true;
+ return true;
+}
+
+void MinidumpLinuxMapsList::Print() const {
+ if (!valid_ || (maps_ == NULL)) {
+ BPLOG(ERROR) << "MinidumpLinuxMapsList cannot print valid data";
+ return;
+ }
+ for (size_t i = 0; i < maps_->size(); i++) {
+ (*maps_)[i]->Print();
+ }
+}
+
+//
+// Minidump
+//
+
+
+uint32_t Minidump::max_streams_ = 128;
+unsigned int Minidump::max_string_length_ = 1024;
+
+
+Minidump::Minidump(const string& path)
+ : header_(),
+ directory_(NULL),
+ stream_map_(new MinidumpStreamMap()),
+ path_(path),
+ stream_(NULL),
+ swap_(false),
+ valid_(false) {
+}
+
+Minidump::Minidump(istream& stream)
+ : header_(),
+ directory_(NULL),
+ stream_map_(new MinidumpStreamMap()),
+ path_(),
+ stream_(&stream),
+ swap_(false),
+ valid_(false) {
+}
+
+Minidump::~Minidump() {
+ if (stream_) {
+ BPLOG(INFO) << "Minidump closing minidump";
+ }
+ if (!path_.empty()) {
+ delete stream_;
+ }
+ delete directory_;
+ delete stream_map_;
+}
+
+
+bool Minidump::Open() {
+ if (stream_ != NULL) {
+ BPLOG(INFO) << "Minidump reopening minidump " << path_;
+
+ // The file is already open. Seek to the beginning, which is the position
+ // the file would be at if it were opened anew.
+ return SeekSet(0);
+ }
+
+ stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary);
+ if (!stream_ || !stream_->good()) {
+ string error_string;
+ int error_code = ErrnoString(&error_string);
+ BPLOG(ERROR) << "Minidump could not open minidump " << path_ <<
+ ", error " << error_code << ": " << error_string;
+ return false;
+ }
+
+ BPLOG(INFO) << "Minidump opened minidump " << path_;
+ return true;
+}
+
+bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
+ // Initialize output parameters
+ *context_cpu_flags = 0;
+
+ // Save the current stream position
+ off_t saved_position = Tell();
+ if (saved_position == -1) {
+ // Failed to save the current stream position.
+ // Returns true because the current position of the stream is preserved.
+ return true;
+ }
+
+ const MDRawSystemInfo* system_info =
+ GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
+
+ if (system_info != NULL) {
+ switch (system_info->processor_architecture) {
+ case MD_CPU_ARCHITECTURE_X86:
+ *context_cpu_flags = MD_CONTEXT_X86;
+ break;
+ case MD_CPU_ARCHITECTURE_MIPS:
+ *context_cpu_flags = MD_CONTEXT_MIPS;
+ break;
+ case MD_CPU_ARCHITECTURE_MIPS64:
+ *context_cpu_flags = MD_CONTEXT_MIPS64;
+ break;
+ case MD_CPU_ARCHITECTURE_ALPHA:
+ *context_cpu_flags = MD_CONTEXT_ALPHA;
+ break;
+ case MD_CPU_ARCHITECTURE_PPC:
+ *context_cpu_flags = MD_CONTEXT_PPC;
+ break;
+ case MD_CPU_ARCHITECTURE_PPC64:
+ *context_cpu_flags = MD_CONTEXT_PPC64;
+ break;
+ case MD_CPU_ARCHITECTURE_SHX:
+ *context_cpu_flags = MD_CONTEXT_SHX;
+ break;
+ case MD_CPU_ARCHITECTURE_ARM:
+ *context_cpu_flags = MD_CONTEXT_ARM;
+ break;
+ case MD_CPU_ARCHITECTURE_ARM64:
+ *context_cpu_flags = MD_CONTEXT_ARM64;
+ break;
+ case MD_CPU_ARCHITECTURE_IA64:
+ *context_cpu_flags = MD_CONTEXT_IA64;
+ break;
+ case MD_CPU_ARCHITECTURE_ALPHA64:
+ *context_cpu_flags = 0;
+ break;
+ case MD_CPU_ARCHITECTURE_MSIL:
+ *context_cpu_flags = 0;
+ break;
+ case MD_CPU_ARCHITECTURE_AMD64:
+ *context_cpu_flags = MD_CONTEXT_AMD64;
+ break;
+ case MD_CPU_ARCHITECTURE_X86_WIN64:
+ *context_cpu_flags = 0;
+ break;
+ case MD_CPU_ARCHITECTURE_SPARC:
+ *context_cpu_flags = MD_CONTEXT_SPARC;
+ break;
+ case MD_CPU_ARCHITECTURE_UNKNOWN:
+ *context_cpu_flags = 0;
+ break;
+ default:
+ *context_cpu_flags = 0;
+ break;
+ }
+ }
+
+ // Restore position and return
+ return SeekSet(saved_position);
+}
+
+
+bool Minidump::Read() {
+ // Invalidate cached data.
+ delete directory_;
+ directory_ = NULL;
+ stream_map_->clear();
+
+ valid_ = false;
+
+ if (!Open()) {
+ BPLOG(ERROR) << "Minidump cannot open minidump";
+ return false;
+ }
+
+ if (!ReadBytes(&header_, sizeof(MDRawHeader))) {
+ BPLOG(ERROR) << "Minidump cannot read header";
+ return false;
+ }
+
+ if (header_.signature != MD_HEADER_SIGNATURE) {
+ // The file may be byte-swapped. Under the present architecture, these
+ // classes don't know or need to know what CPU (or endianness) the
+ // minidump was produced on in order to parse it. Use the signature as
+ // a byte order marker.
+ uint32_t signature_swapped = header_.signature;
+ Swap(&signature_swapped);
+ if (signature_swapped != MD_HEADER_SIGNATURE) {
+ // This isn't a minidump or a byte-swapped minidump.
+ BPLOG(ERROR) << "Minidump header signature mismatch: (" <<
+ HexString(header_.signature) << ", " <<
+ HexString(signature_swapped) << ") != " <<
+ HexString(MD_HEADER_SIGNATURE);
+ return false;
+ }
+ swap_ = true;
+ } else {
+ // The file is not byte-swapped. Set swap_ false (it may have been true
+ // if the object is being reused?)
+ swap_ = false;
+ }
+
+ BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") <<
+ "byte-swapping minidump";
+
+ if (swap_) {
+ Swap(&header_.signature);
+ Swap(&header_.version);
+ Swap(&header_.stream_count);
+ Swap(&header_.stream_directory_rva);
+ Swap(&header_.checksum);
+ Swap(&header_.time_date_stamp);
+ Swap(&header_.flags);
+ }
+
+ // Version check. The high 16 bits of header_.version contain something
+ // else "implementation specific."
+ if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) {
+ BPLOG(ERROR) << "Minidump version mismatch: " <<
+ HexString(header_.version & 0x0000ffff) << " != " <<
+ HexString(MD_HEADER_VERSION);
+ return false;
+ }
+
+ if (!SeekSet(header_.stream_directory_rva)) {
+ BPLOG(ERROR) << "Minidump cannot seek to stream directory";
+ return false;
+ }
+
+ if (header_.stream_count > max_streams_) {
+ BPLOG(ERROR) << "Minidump stream count " << header_.stream_count <<
+ " exceeds maximum " << max_streams_;
+ return false;
+ }
+
+ if (header_.stream_count != 0) {
+ scoped_ptr<MinidumpDirectoryEntries> directory(
+ new MinidumpDirectoryEntries(header_.stream_count));
+
+ // Read the entire array in one fell swoop, instead of reading one entry
+ // at a time in the loop.
+ if (!ReadBytes(&(*directory)[0],
+ sizeof(MDRawDirectory) * header_.stream_count)) {
+ BPLOG(ERROR) << "Minidump cannot read stream directory";
+ return false;
+ }
+
+ for (unsigned int stream_index = 0;
+ stream_index < header_.stream_count;
+ ++stream_index) {
+ MDRawDirectory* directory_entry = &(*directory)[stream_index];
+
+ if (swap_) {
+ Swap(&directory_entry->stream_type);
+ Swap(&directory_entry->location);
+ }
+
+ // Initialize the stream_map_ map, which speeds locating a stream by
+ // type.
+ unsigned int stream_type = directory_entry->stream_type;
+ switch (stream_type) {
+ case MD_THREAD_LIST_STREAM:
+ case MD_MODULE_LIST_STREAM:
+ case MD_MEMORY_LIST_STREAM:
+ case MD_EXCEPTION_STREAM:
+ case MD_SYSTEM_INFO_STREAM:
+ case MD_MISC_INFO_STREAM:
+ case MD_BREAKPAD_INFO_STREAM: {
+ if (stream_map_->find(stream_type) != stream_map_->end()) {
+ // Another stream with this type was already found. A minidump
+ // file should contain at most one of each of these stream types.
+ BPLOG(ERROR) << "Minidump found multiple streams of type " <<
+ stream_type << ", but can only deal with one";
+ return false;
+ }
+ // Fall through to default
+ }
+
+ default: {
+ // Overwrites for stream types other than those above, but it's
+ // expected to be the user's burden in that case.
+ (*stream_map_)[stream_type].stream_index = stream_index;
+ }
+ }
+ }
+
+ directory_ = directory.release();
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+MinidumpThreadList* Minidump::GetThreadList() {
+ MinidumpThreadList* thread_list;
+ return GetStream(&thread_list);
+}
+
+
+MinidumpModuleList* Minidump::GetModuleList() {
+ MinidumpModuleList* module_list;
+ return GetStream(&module_list);
+}
+
+
+MinidumpMemoryList* Minidump::GetMemoryList() {
+ MinidumpMemoryList* memory_list;
+ return GetStream(&memory_list);
+}
+
+
+MinidumpException* Minidump::GetException() {
+ MinidumpException* exception;
+ return GetStream(&exception);
+}
+
+MinidumpAssertion* Minidump::GetAssertion() {
+ MinidumpAssertion* assertion;
+ return GetStream(&assertion);
+}
+
+
+MinidumpSystemInfo* Minidump::GetSystemInfo() {
+ MinidumpSystemInfo* system_info;
+ return GetStream(&system_info);
+}
+
+
+MinidumpMiscInfo* Minidump::GetMiscInfo() {
+ MinidumpMiscInfo* misc_info;
+ return GetStream(&misc_info);
+}
+
+
+MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() {
+ MinidumpBreakpadInfo* breakpad_info;
+ return GetStream(&breakpad_info);
+}
+
+MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() {
+ MinidumpMemoryInfoList* memory_info_list;
+ return GetStream(&memory_info_list);
+}
+
+MinidumpLinuxMapsList *Minidump::GetLinuxMapsList() {
+ MinidumpLinuxMapsList *linux_maps_list;
+ return GetStream(&linux_maps_list);
+}
+
+bool Minidump::IsAndroid() {
+ // Save the current stream position
+ off_t saved_position = Tell();
+ if (saved_position == -1) {
+ return false;
+ }
+ const MDRawSystemInfo* system_info =
+ GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
+
+ // Restore position and return
+ if (!SeekSet(saved_position)) {
+ BPLOG(ERROR) << "Couldn't seek back to saved position";
+ return false;
+ }
+
+ return system_info && system_info->platform_id == MD_OS_ANDROID;
+}
+
+static const char* get_stream_name(uint32_t stream_type) {
+ switch (stream_type) {
+ case MD_UNUSED_STREAM:
+ return "MD_UNUSED_STREAM";
+ case MD_RESERVED_STREAM_0:
+ return "MD_RESERVED_STREAM_0";
+ case MD_RESERVED_STREAM_1:
+ return "MD_RESERVED_STREAM_1";
+ case MD_THREAD_LIST_STREAM:
+ return "MD_THREAD_LIST_STREAM";
+ case MD_MODULE_LIST_STREAM:
+ return "MD_MODULE_LIST_STREAM";
+ case MD_MEMORY_LIST_STREAM:
+ return "MD_MEMORY_LIST_STREAM";
+ case MD_EXCEPTION_STREAM:
+ return "MD_EXCEPTION_STREAM";
+ case MD_SYSTEM_INFO_STREAM:
+ return "MD_SYSTEM_INFO_STREAM";
+ case MD_THREAD_EX_LIST_STREAM:
+ return "MD_THREAD_EX_LIST_STREAM";
+ case MD_MEMORY_64_LIST_STREAM:
+ return "MD_MEMORY_64_LIST_STREAM";
+ case MD_COMMENT_STREAM_A:
+ return "MD_COMMENT_STREAM_A";
+ case MD_COMMENT_STREAM_W:
+ return "MD_COMMENT_STREAM_W";
+ case MD_HANDLE_DATA_STREAM:
+ return "MD_HANDLE_DATA_STREAM";
+ case MD_FUNCTION_TABLE_STREAM:
+ return "MD_FUNCTION_TABLE_STREAM";
+ case MD_UNLOADED_MODULE_LIST_STREAM:
+ return "MD_UNLOADED_MODULE_LIST_STREAM";
+ case MD_MISC_INFO_STREAM:
+ return "MD_MISC_INFO_STREAM";
+ case MD_MEMORY_INFO_LIST_STREAM:
+ return "MD_MEMORY_INFO_LIST_STREAM";
+ case MD_THREAD_INFO_LIST_STREAM:
+ return "MD_THREAD_INFO_LIST_STREAM";
+ case MD_HANDLE_OPERATION_LIST_STREAM:
+ return "MD_HANDLE_OPERATION_LIST_STREAM";
+ case MD_TOKEN_STREAM:
+ return "MD_TOKEN_STREAM";
+ case MD_JAVASCRIPT_DATA_STREAM:
+ return "MD_JAVASCRIPT_DATA_STREAM";
+ case MD_SYSTEM_MEMORY_INFO_STREAM:
+ return "MD_SYSTEM_MEMORY_INFO_STREAM";
+ case MD_PROCESS_VM_COUNTERS_STREAM:
+ return "MD_PROCESS_VM_COUNTERS_STREAM";
+ case MD_LAST_RESERVED_STREAM:
+ return "MD_LAST_RESERVED_STREAM";
+ case MD_BREAKPAD_INFO_STREAM:
+ return "MD_BREAKPAD_INFO_STREAM";
+ case MD_ASSERTION_INFO_STREAM:
+ return "MD_ASSERTION_INFO_STREAM";
+ case MD_LINUX_CPU_INFO:
+ return "MD_LINUX_CPU_INFO";
+ case MD_LINUX_PROC_STATUS:
+ return "MD_LINUX_PROC_STATUS";
+ case MD_LINUX_LSB_RELEASE:
+ return "MD_LINUX_LSB_RELEASE";
+ case MD_LINUX_CMD_LINE:
+ return "MD_LINUX_CMD_LINE";
+ case MD_LINUX_ENVIRON:
+ return "MD_LINUX_ENVIRON";
+ case MD_LINUX_AUXV:
+ return "MD_LINUX_AUXV";
+ case MD_LINUX_MAPS:
+ return "MD_LINUX_MAPS";
+ case MD_LINUX_DSO_DEBUG:
+ return "MD_LINUX_DSO_DEBUG";
+ default:
+ return "unknown";
+ }
+}
+
+void Minidump::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Minidump cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawHeader\n");
+ printf(" signature = 0x%x\n", header_.signature);
+ printf(" version = 0x%x\n", header_.version);
+ printf(" stream_count = %d\n", header_.stream_count);
+ printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva);
+ printf(" checksum = 0x%x\n", header_.checksum);
+ printf(" time_date_stamp = 0x%x %s\n",
+ header_.time_date_stamp,
+ TimeTToUTCString(header_.time_date_stamp).c_str());
+ printf(" flags = 0x%" PRIx64 "\n", header_.flags);
+ printf("\n");
+
+ for (unsigned int stream_index = 0;
+ stream_index < header_.stream_count;
+ ++stream_index) {
+ MDRawDirectory* directory_entry = &(*directory_)[stream_index];
+
+ printf("mDirectory[%d]\n", stream_index);
+ printf("MDRawDirectory\n");
+ printf(" stream_type = 0x%x (%s)\n", directory_entry->stream_type,
+ get_stream_name(directory_entry->stream_type));
+ printf(" location.data_size = %d\n",
+ directory_entry->location.data_size);
+ printf(" location.rva = 0x%x\n", directory_entry->location.rva);
+ printf("\n");
+ }
+
+ printf("Streams:\n");
+ for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin();
+ iterator != stream_map_->end();
+ ++iterator) {
+ uint32_t stream_type = iterator->first;
+ const MinidumpStreamInfo& info = iterator->second;
+ printf(" stream type 0x%x (%s) at index %d\n", stream_type,
+ get_stream_name(stream_type),
+ info.stream_index);
+ }
+ printf("\n");
+}
+
+
+const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index)
+ const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex";
+ return NULL;
+ }
+
+ if (index >= header_.stream_count) {
+ BPLOG(ERROR) << "Minidump stream directory index out of range: " <<
+ index << "/" << header_.stream_count;
+ return NULL;
+ }
+
+ return &(*directory_)[index];
+}
+
+
+bool Minidump::ReadBytes(void* bytes, size_t count) {
+ // Can't check valid_ because Read needs to call this method before
+ // validity can be determined.
+ if (!stream_) {
+ return false;
+ }
+ stream_->read(static_cast<char*>(bytes), count);
+ std::streamsize bytes_read = stream_->gcount();
+ if (bytes_read == -1) {
+ string error_string;
+ int error_code = ErrnoString(&error_string);
+ BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string;
+ return false;
+ }
+
+ // Convert to size_t and check for data loss
+ size_t bytes_read_converted = static_cast<size_t>(bytes_read);
+ if (static_cast<std::streamsize>(bytes_read_converted) != bytes_read) {
+ BPLOG(ERROR) << "ReadBytes: conversion data loss detected when converting "
+ << bytes_read << " to " << bytes_read_converted;
+ return false;
+ }
+
+ if (bytes_read_converted != count) {
+ BPLOG(ERROR) << "ReadBytes: read " << bytes_read_converted << "/" << count;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool Minidump::SeekSet(off_t offset) {
+ // Can't check valid_ because Read needs to call this method before
+ // validity can be determined.
+ if (!stream_) {
+ return false;
+ }
+ stream_->seekg(offset, std::ios_base::beg);
+ if (!stream_->good()) {
+ string error_string;
+ int error_code = ErrnoString(&error_string);
+ BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string;
+ return false;
+ }
+ return true;
+}
+
+off_t Minidump::Tell() {
+ if (!valid_ || !stream_) {
+ return (off_t)-1;
+ }
+
+ // Check for conversion data loss
+ std::streamoff std_streamoff = stream_->tellg();
+ off_t rv = static_cast<off_t>(std_streamoff);
+ if (static_cast<std::streamoff>(rv) == std_streamoff) {
+ return rv;
+ } else {
+ BPLOG(ERROR) << "Data loss detected";
+ return (off_t)-1;
+ }
+}
+
+
+string* Minidump::ReadString(off_t offset) {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid Minidump for ReadString";
+ return NULL;
+ }
+ if (!SeekSet(offset)) {
+ BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset;
+ return NULL;
+ }
+
+ uint32_t bytes;
+ if (!ReadBytes(&bytes, sizeof(bytes))) {
+ BPLOG(ERROR) << "ReadString could not read string size at offset " <<
+ offset;
+ return NULL;
+ }
+ if (swap_)
+ Swap(&bytes);
+
+ if (bytes % 2 != 0) {
+ BPLOG(ERROR) << "ReadString found odd-sized " << bytes <<
+ "-byte string at offset " << offset;
+ return NULL;
+ }
+ unsigned int utf16_words = bytes / 2;
+
+ if (utf16_words > max_string_length_) {
+ BPLOG(ERROR) << "ReadString string length " << utf16_words <<
+ " exceeds maximum " << max_string_length_ <<
+ " at offset " << offset;
+ return NULL;
+ }
+
+ vector<uint16_t> string_utf16(utf16_words);
+
+ if (utf16_words) {
+ if (!ReadBytes(&string_utf16[0], bytes)) {
+ BPLOG(ERROR) << "ReadString could not read " << bytes <<
+ "-byte string at offset " << offset;
+ return NULL;
+ }
+ }
+
+ return UTF16ToUTF8(string_utf16, swap_);
+}
+
+
+bool Minidump::SeekToStreamType(uint32_t stream_type,
+ uint32_t* stream_length) {
+ BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires "
+ "|stream_length|";
+ assert(stream_length);
+ *stream_length = 0;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType";
+ return false;
+ }
+
+ MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type);
+ if (iterator == stream_map_->end()) {
+ // This stream type didn't exist in the directory.
+ BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present";
+ return false;
+ }
+
+ const MinidumpStreamInfo& info = iterator->second;
+ if (info.stream_index >= header_.stream_count) {
+ BPLOG(ERROR) << "SeekToStreamType: type " << stream_type <<
+ " out of range: " <<
+ info.stream_index << "/" << header_.stream_count;
+ return false;
+ }
+
+ MDRawDirectory* directory_entry = &(*directory_)[info.stream_index];
+ if (!SeekSet(directory_entry->location.rva)) {
+ BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " <<
+ stream_type;
+ return false;
+ }
+
+ *stream_length = directory_entry->location.data_size;
+
+ return true;
+}
+
+
+template<typename T>
+T* Minidump::GetStream(T** stream) {
+ // stream is a garbage parameter that's present only to account for C++'s
+ // inability to overload a method based solely on its return type.
+
+ const uint32_t stream_type = T::kStreamType;
+
+ BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type <<
+ " requires |stream|";
+ assert(stream);
+ *stream = NULL;
+
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type;
+ return NULL;
+ }
+
+ MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type);
+ if (iterator == stream_map_->end()) {
+ // This stream type didn't exist in the directory.
+ BPLOG(INFO) << "GetStream: type " << stream_type << " not present";
+ return NULL;
+ }
+
+ // Get a pointer so that the stored stream field can be altered.
+ MinidumpStreamInfo* info = &iterator->second;
+
+ if (info->stream) {
+ // This cast is safe because info.stream is only populated by this
+ // method, and there is a direct correlation between T and stream_type.
+ *stream = static_cast<T*>(info->stream);
+ return *stream;
+ }
+
+ uint32_t stream_length;
+ if (!SeekToStreamType(stream_type, &stream_length)) {
+ BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type;
+ return NULL;
+ }
+
+ scoped_ptr<T> new_stream(new T(this));
+
+ if (!new_stream->Read(stream_length)) {
+ BPLOG(ERROR) << "GetStream could not read stream type " << stream_type;
+ return NULL;
+ }
+
+ *stream = new_stream.release();
+ info->stream = *stream;
+ return *stream;
+}
+
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc
new file mode 100644
index 000000000..343f04428
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// minidump_dump.cc: Print the contents of a minidump file in somewhat
+// readable text.
+//
+// Author: Mark Mentovai
+
+#include <stdio.h>
+#include <string.h>
+
+#include "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::MinidumpModuleList;
+using google_breakpad::MinidumpMemoryInfoList;
+using google_breakpad::MinidumpMemoryList;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpAssertion;
+using google_breakpad::MinidumpSystemInfo;
+using google_breakpad::MinidumpMiscInfo;
+using google_breakpad::MinidumpBreakpadInfo;
+
+static void DumpRawStream(Minidump *minidump,
+ 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 char *minidump_file) {
+ Minidump minidump(minidump_file);
+ if (!minidump.Read()) {
+ BPLOG(ERROR) << "minidump.Read() failed";
+ return false;
+ }
+ minidump.Print();
+
+ int errors = 0;
+
+ MinidumpThreadList *thread_list = minidump.GetThreadList();
+ if (!thread_list) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetThreadList() failed";
+ } else {
+ thread_list->Print();
+ }
+
+ MinidumpModuleList *module_list = minidump.GetModuleList();
+ if (!module_list) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetModuleList() failed";
+ } else {
+ module_list->Print();
+ }
+
+ MinidumpMemoryList *memory_list = minidump.GetMemoryList();
+ if (!memory_list) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetMemoryList() failed";
+ } else {
+ memory_list->Print();
+ }
+
+ MinidumpException *exception = minidump.GetException();
+ if (!exception) {
+ BPLOG(INFO) << "minidump.GetException() failed";
+ } else {
+ exception->Print();
+ }
+
+ MinidumpAssertion *assertion = minidump.GetAssertion();
+ if (!assertion) {
+ BPLOG(INFO) << "minidump.GetAssertion() failed";
+ } else {
+ assertion->Print();
+ }
+
+ MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
+ if (!system_info) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetSystemInfo() failed";
+ } else {
+ system_info->Print();
+ }
+
+ MinidumpMiscInfo *misc_info = minidump.GetMiscInfo();
+ if (!misc_info) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetMiscInfo() failed";
+ } else {
+ misc_info->Print();
+ }
+
+ MinidumpBreakpadInfo *breakpad_info = minidump.GetBreakpadInfo();
+ if (!breakpad_info) {
+ // Breakpad info is optional, so don't treat this as an error.
+ BPLOG(INFO) << "minidump.GetBreakpadInfo() failed";
+ } else {
+ breakpad_info->Print();
+ }
+
+ MinidumpMemoryInfoList *memory_info_list = minidump.GetMemoryInfoList();
+ if (!memory_info_list) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetMemoryInfoList() failed";
+ } else {
+ memory_info_list->Print();
+ }
+
+ DumpRawStream(&minidump,
+ MD_LINUX_CMD_LINE,
+ "MD_LINUX_CMD_LINE",
+ &errors);
+ DumpRawStream(&minidump,
+ MD_LINUX_ENVIRON,
+ "MD_LINUX_ENVIRON",
+ &errors);
+ DumpRawStream(&minidump,
+ MD_LINUX_LSB_RELEASE,
+ "MD_LINUX_LSB_RELEASE",
+ &errors);
+ DumpRawStream(&minidump,
+ MD_LINUX_PROC_STATUS,
+ "MD_LINUX_PROC_STATUS",
+ &errors);
+ DumpRawStream(&minidump,
+ MD_LINUX_CPU_INFO,
+ "MD_LINUX_CPU_INFO",
+ &errors);
+ DumpRawStream(&minidump,
+ MD_LINUX_MAPS,
+ "MD_LINUX_MAPS",
+ &errors);
+
+ return errors == 0;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <file>\n", argv[0]);
+ return 1;
+ }
+
+ return PrintMinidumpDump(argv[1]) ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump_test b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump_test
new file mode 100755
index 000000000..fb62ace73
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump_test
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# Copyright (c) 2006, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \
+ tr -d '\015' | \
+ diff -u $testdata_dir/minidump2.dump.out -
+exit $?
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
new file mode 100644
index 000000000..33b4a1284
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
@@ -0,0 +1,1577 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google_breakpad/processor/minidump_processor.h"
+
+#include <assert.h>
+
+#include <string>
+
+#include "common/scoped_ptr.h"
+#include "common/stdio_wrapper.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/minidump.h"
+#include "google_breakpad/processor/process_state.h"
+#include "google_breakpad/processor/exploitability.h"
+#include "google_breakpad/processor/stack_frame_symbolizer.h"
+#include "processor/logging.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/symbolic_constants_win.h"
+
+namespace google_breakpad {
+
+MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier,
+ SourceLineResolverInterface *resolver)
+ : frame_symbolizer_(new StackFrameSymbolizer(supplier, resolver)),
+ own_frame_symbolizer_(true),
+ enable_exploitability_(false),
+ enable_objdump_(false) {
+}
+
+MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier,
+ SourceLineResolverInterface *resolver,
+ bool enable_exploitability)
+ : frame_symbolizer_(new StackFrameSymbolizer(supplier, resolver)),
+ own_frame_symbolizer_(true),
+ enable_exploitability_(enable_exploitability),
+ enable_objdump_(false) {
+}
+
+MinidumpProcessor::MinidumpProcessor(StackFrameSymbolizer *frame_symbolizer,
+ bool enable_exploitability)
+ : frame_symbolizer_(frame_symbolizer),
+ own_frame_symbolizer_(false),
+ enable_exploitability_(enable_exploitability),
+ enable_objdump_(false) {
+ assert(frame_symbolizer_);
+}
+
+MinidumpProcessor::~MinidumpProcessor() {
+ if (own_frame_symbolizer_) delete frame_symbolizer_;
+}
+
+ProcessResult MinidumpProcessor::Process(
+ Minidump *dump, ProcessState *process_state) {
+ assert(dump);
+ assert(process_state);
+
+ process_state->Clear();
+
+ const MDRawHeader *header = dump->header();
+ if (!header) {
+ BPLOG(ERROR) << "Minidump " << dump->path() << " has no header";
+ return PROCESS_ERROR_NO_MINIDUMP_HEADER;
+ }
+ process_state->time_date_stamp_ = header->time_date_stamp;
+
+ bool has_process_create_time =
+ GetProcessCreateTime(dump, &process_state->process_create_time_);
+
+ bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_);
+ bool has_os_info = GetOSInfo(dump, &process_state->system_info_);
+
+ uint32_t dump_thread_id = 0;
+ bool has_dump_thread = false;
+ uint32_t requesting_thread_id = 0;
+ bool has_requesting_thread = false;
+
+ MinidumpBreakpadInfo *breakpad_info = dump->GetBreakpadInfo();
+ if (breakpad_info) {
+ has_dump_thread = breakpad_info->GetDumpThreadID(&dump_thread_id);
+ has_requesting_thread =
+ breakpad_info->GetRequestingThreadID(&requesting_thread_id);
+ }
+
+ MinidumpException *exception = dump->GetException();
+ if (exception) {
+ process_state->crashed_ = true;
+ has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
+
+ process_state->crash_reason_ = GetCrashReason(
+ dump, &process_state->crash_address_);
+ }
+
+ // This will just return an empty string if it doesn't exist.
+ process_state->assertion_ = GetAssertion(dump);
+
+ MinidumpModuleList *module_list = dump->GetModuleList();
+
+ // Put a copy of the module list into ProcessState object. This is not
+ // necessarily a MinidumpModuleList, but it adheres to the CodeModules
+ // interface, which is all that ProcessState needs to expose.
+ if (module_list) {
+ process_state->modules_ = module_list->Copy();
+ process_state->shrunk_range_modules_ =
+ process_state->modules_->GetShrunkRangeModules();
+ for (unsigned int i = 0;
+ i < process_state->shrunk_range_modules_.size();
+ i++) {
+ linked_ptr<const CodeModule> module =
+ process_state->shrunk_range_modules_[i];
+ BPLOG(INFO) << "The range for module " << module->code_file()
+ << " was shrunk down by " << HexString(
+ module->shrink_down_delta()) << " bytes. ";
+ }
+ }
+
+ MinidumpMemoryList *memory_list = dump->GetMemoryList();
+ if (memory_list) {
+ BPLOG(INFO) << "Found " << memory_list->region_count()
+ << " memory regions.";
+ }
+
+ MinidumpThreadList *threads = dump->GetThreadList();
+ if (!threads) {
+ BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list";
+ return PROCESS_ERROR_NO_THREAD_LIST;
+ }
+
+ BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
+ (has_cpu_info ? "" : "no ") << "CPU info, " <<
+ (has_os_info ? "" : "no ") << "OS info, " <<
+ (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
+ (exception != NULL ? "" : "no ") << "exception, " <<
+ (module_list != NULL ? "" : "no ") << "module list, " <<
+ (threads != NULL ? "" : "no ") << "thread list, " <<
+ (has_dump_thread ? "" : "no ") << "dump thread, " <<
+ (has_requesting_thread ? "" : "no ") << "requesting thread, and " <<
+ (has_process_create_time ? "" : "no ") << "process create time";
+
+ bool interrupted = false;
+ bool found_requesting_thread = false;
+ unsigned int thread_count = threads->thread_count();
+
+ // Reset frame_symbolizer_ at the beginning of stackwalk for each minidump.
+ frame_symbolizer_->Reset();
+
+ for (unsigned int thread_index = 0;
+ thread_index < thread_count;
+ ++thread_index) {
+ char thread_string_buffer[64];
+ snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d",
+ thread_index, thread_count);
+ string thread_string = dump->path() + ":" + thread_string_buffer;
+
+ MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
+ if (!thread) {
+ BPLOG(ERROR) << "Could not get thread for " << thread_string;
+ return PROCESS_ERROR_GETTING_THREAD;
+ }
+
+ uint32_t thread_id;
+ if (!thread->GetThreadID(&thread_id)) {
+ BPLOG(ERROR) << "Could not get thread ID for " << thread_string;
+ return PROCESS_ERROR_GETTING_THREAD_ID;
+ }
+
+ thread_string += " id " + HexString(thread_id);
+ BPLOG(INFO) << "Looking at thread " << thread_string;
+
+ // If this thread is the thread that produced the minidump, don't process
+ // it. Because of the problems associated with a thread producing a
+ // dump of itself (when both its context and its stack are in flux),
+ // processing that stack wouldn't provide much useful data.
+ if (has_dump_thread && thread_id == dump_thread_id) {
+ continue;
+ }
+
+ MinidumpContext *context = thread->GetContext();
+
+ if (has_requesting_thread && thread_id == requesting_thread_id) {
+ if (found_requesting_thread) {
+ // There can't be more than one requesting thread.
+ BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string;
+ return PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS;
+ }
+
+ // Use processed_state->threads_.size() instead of thread_index.
+ // thread_index points to the thread index in the minidump, which
+ // might be greater than the thread index in the threads vector if
+ // any of the minidump's threads are skipped and not placed into the
+ // processed threads vector. The thread vector's current size will
+ // be the index of the current thread when it's pushed into the
+ // vector.
+ process_state->requesting_thread_ = process_state->threads_.size();
+
+ found_requesting_thread = true;
+
+ if (process_state->crashed_) {
+ // Use the exception record's context for the crashed thread, instead
+ // of the thread's own context. For the crashed thread, the thread's
+ // own context is the state inside the exception handler. Using it
+ // would not result in the expected stack trace from the time of the
+ // crash. If the exception context is invalid, however, we fall back
+ // on the thread context.
+ MinidumpContext *ctx = exception->GetContext();
+ context = ctx ? ctx : thread->GetContext();
+ }
+ }
+
+ // If the memory region for the stack cannot be read using the RVA stored
+ // in the memory descriptor inside MINIDUMP_THREAD, try to locate and use
+ // a memory region (containing the stack) from the minidump memory list.
+ MinidumpMemoryRegion *thread_memory = thread->GetMemory();
+ if (!thread_memory && memory_list) {
+ uint64_t start_stack_memory_range = thread->GetStartOfStackMemoryRange();
+ if (start_stack_memory_range) {
+ thread_memory = memory_list->GetMemoryRegionForAddress(
+ start_stack_memory_range);
+ }
+ }
+ if (!thread_memory) {
+ BPLOG(ERROR) << "No memory region for " << thread_string;
+ }
+
+ // Use process_state->modules_ instead of module_list, because the
+ // |modules| argument will be used to populate the |module| fields in
+ // the returned StackFrame objects, which will be placed into the
+ // returned ProcessState object. module_list's lifetime is only as
+ // long as the Minidump object: it will be deleted when this function
+ // returns. process_state->modules_ is owned by the ProcessState object
+ // (just like the StackFrame objects), and is much more suitable for this
+ // task.
+ scoped_ptr<Stackwalker> stackwalker(
+ Stackwalker::StackwalkerForCPU(process_state->system_info(),
+ context,
+ thread_memory,
+ process_state->modules_,
+ 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) << "Stackwalker interrupt (missing symbols?) at "
+ << thread_string;
+ interrupted = true;
+ }
+ } else {
+ // Threads with missing CPU contexts will hit this, but
+ // don't abort processing the rest of the dump just for
+ // one bad thread.
+ BPLOG(ERROR) << "No stackwalker for " << thread_string;
+ }
+ stack->set_tid(thread_id);
+ process_state->threads_.push_back(stack.release());
+ process_state->thread_memory_regions_.push_back(thread_memory);
+ }
+
+ if (interrupted) {
+ BPLOG(INFO) << "Processing interrupted for " << dump->path();
+ return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
+ }
+
+ // If a requesting thread was indicated, it must be present.
+ if (has_requesting_thread && !found_requesting_thread) {
+ // Don't mark as an error, but invalidate the requesting thread
+ BPLOG(ERROR) << "Minidump indicated requesting thread " <<
+ HexString(requesting_thread_id) << ", not found in " <<
+ dump->path();
+ process_state->requesting_thread_ = -1;
+ }
+
+ // Exploitability defaults to EXPLOITABILITY_NOT_ANALYZED
+ process_state->exploitability_ = EXPLOITABILITY_NOT_ANALYZED;
+
+ // If an exploitability run was requested we perform the platform specific
+ // rating.
+ if (enable_exploitability_) {
+ scoped_ptr<Exploitability> exploitability(
+ Exploitability::ExploitabilityForPlatform(dump,
+ process_state,
+ enable_objdump_));
+ // The engine will be null if the platform is not supported
+ if (exploitability != NULL) {
+ process_state->exploitability_ = exploitability->CheckExploitability();
+ } else {
+ process_state->exploitability_ = EXPLOITABILITY_ERR_NOENGINE;
+ }
+ }
+
+ BPLOG(INFO) << "Processed " << dump->path();
+ return PROCESS_OK;
+}
+
+ProcessResult MinidumpProcessor::Process(
+ const string &minidump_file, ProcessState *process_state) {
+ BPLOG(INFO) << "Processing minidump in file " << minidump_file;
+
+ Minidump dump(minidump_file);
+ if (!dump.Read()) {
+ BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read";
+ return PROCESS_ERROR_MINIDUMP_NOT_FOUND;
+ }
+
+ return Process(&dump, process_state);
+}
+
+// Returns the MDRawSystemInfo from a minidump, or NULL if system info is
+// not available from the minidump. If system_info is non-NULL, it is used
+// to pass back the MinidumpSystemInfo object.
+static const MDRawSystemInfo* GetSystemInfo(Minidump *dump,
+ MinidumpSystemInfo **system_info) {
+ MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo();
+ if (!minidump_system_info)
+ return NULL;
+
+ if (system_info)
+ *system_info = minidump_system_info;
+
+ return minidump_system_info->system_info();
+}
+
+// Extract CPU info string from ARM-specific MDRawSystemInfo structure.
+// raw_info: pointer to source MDRawSystemInfo.
+// cpu_info: address of target string, cpu info text will be appended to it.
+static void GetARMCpuInfo(const MDRawSystemInfo* raw_info,
+ string* cpu_info) {
+ assert(raw_info != NULL && cpu_info != NULL);
+
+ // Write ARM architecture version.
+ char cpu_string[32];
+ snprintf(cpu_string, sizeof(cpu_string), "ARMv%d",
+ raw_info->processor_level);
+ cpu_info->append(cpu_string);
+
+ // There is no good list of implementer id values, but the following
+ // pages provide some help:
+ // http://comments.gmane.org/gmane.linux.linaro.devel/6903
+ // http://forum.xda-developers.com/archive/index.php/t-480226.html
+ const struct {
+ uint32_t id;
+ const char* name;
+ } vendors[] = {
+ { 0x41, "ARM" },
+ { 0x51, "Qualcomm" },
+ { 0x56, "Marvell" },
+ { 0x69, "Intel/Marvell" },
+ };
+ const struct {
+ uint32_t id;
+ const char* name;
+ } parts[] = {
+ { 0x4100c050, "Cortex-A5" },
+ { 0x4100c080, "Cortex-A8" },
+ { 0x4100c090, "Cortex-A9" },
+ { 0x4100c0f0, "Cortex-A15" },
+ { 0x4100c140, "Cortex-R4" },
+ { 0x4100c150, "Cortex-R5" },
+ { 0x4100b360, "ARM1136" },
+ { 0x4100b560, "ARM1156" },
+ { 0x4100b760, "ARM1176" },
+ { 0x4100b020, "ARM11-MPCore" },
+ { 0x41009260, "ARM926" },
+ { 0x41009460, "ARM946" },
+ { 0x41009660, "ARM966" },
+ { 0x510006f0, "Krait" },
+ { 0x510000f0, "Scorpion" },
+ };
+
+ const struct {
+ uint32_t hwcap;
+ const char* name;
+ } features[] = {
+ { MD_CPU_ARM_ELF_HWCAP_SWP, "swp" },
+ { MD_CPU_ARM_ELF_HWCAP_HALF, "half" },
+ { MD_CPU_ARM_ELF_HWCAP_THUMB, "thumb" },
+ { MD_CPU_ARM_ELF_HWCAP_26BIT, "26bit" },
+ { MD_CPU_ARM_ELF_HWCAP_FAST_MULT, "fastmult" },
+ { MD_CPU_ARM_ELF_HWCAP_FPA, "fpa" },
+ { MD_CPU_ARM_ELF_HWCAP_VFP, "vfpv2" },
+ { MD_CPU_ARM_ELF_HWCAP_EDSP, "edsp" },
+ { MD_CPU_ARM_ELF_HWCAP_JAVA, "java" },
+ { MD_CPU_ARM_ELF_HWCAP_IWMMXT, "iwmmxt" },
+ { MD_CPU_ARM_ELF_HWCAP_CRUNCH, "crunch" },
+ { MD_CPU_ARM_ELF_HWCAP_THUMBEE, "thumbee" },
+ { MD_CPU_ARM_ELF_HWCAP_NEON, "neon" },
+ { MD_CPU_ARM_ELF_HWCAP_VFPv3, "vfpv3" },
+ { MD_CPU_ARM_ELF_HWCAP_VFPv3D16, "vfpv3d16" },
+ { MD_CPU_ARM_ELF_HWCAP_TLS, "tls" },
+ { MD_CPU_ARM_ELF_HWCAP_VFPv4, "vfpv4" },
+ { MD_CPU_ARM_ELF_HWCAP_IDIVA, "idiva" },
+ { MD_CPU_ARM_ELF_HWCAP_IDIVT, "idivt" },
+ };
+
+ uint32_t cpuid = raw_info->cpu.arm_cpu_info.cpuid;
+ if (cpuid != 0) {
+ // Extract vendor name from CPUID
+ const char* vendor = NULL;
+ uint32_t vendor_id = (cpuid >> 24) & 0xff;
+ for (size_t i = 0; i < sizeof(vendors)/sizeof(vendors[0]); ++i) {
+ if (vendors[i].id == vendor_id) {
+ vendor = vendors[i].name;
+ break;
+ }
+ }
+ cpu_info->append(" ");
+ if (vendor) {
+ cpu_info->append(vendor);
+ } else {
+ snprintf(cpu_string, sizeof(cpu_string), "vendor(0x%x)", vendor_id);
+ cpu_info->append(cpu_string);
+ }
+
+ // Extract part name from CPUID
+ uint32_t part_id = (cpuid & 0xff00fff0);
+ const char* part = NULL;
+ for (size_t i = 0; i < sizeof(parts)/sizeof(parts[0]); ++i) {
+ if (parts[i].id == part_id) {
+ part = parts[i].name;
+ break;
+ }
+ }
+ cpu_info->append(" ");
+ if (part != NULL) {
+ cpu_info->append(part);
+ } else {
+ snprintf(cpu_string, sizeof(cpu_string), "part(0x%x)", part_id);
+ cpu_info->append(cpu_string);
+ }
+ }
+ uint32_t elf_hwcaps = raw_info->cpu.arm_cpu_info.elf_hwcaps;
+ if (elf_hwcaps != 0) {
+ cpu_info->append(" features: ");
+ const char* comma = "";
+ for (size_t i = 0; i < sizeof(features)/sizeof(features[0]); ++i) {
+ if (elf_hwcaps & features[i].hwcap) {
+ cpu_info->append(comma);
+ cpu_info->append(features[i].name);
+ comma = ",";
+ }
+ }
+ }
+}
+
+// static
+bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
+ assert(dump);
+ assert(info);
+
+ info->cpu.clear();
+ info->cpu_info.clear();
+
+ MinidumpSystemInfo *system_info;
+ const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
+ if (!raw_system_info)
+ return false;
+
+ switch (raw_system_info->processor_architecture) {
+ case MD_CPU_ARCHITECTURE_X86:
+ case MD_CPU_ARCHITECTURE_AMD64: {
+ if (raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_X86)
+ info->cpu = "x86";
+ else
+ info->cpu = "amd64";
+
+ const string *cpu_vendor = system_info->GetCPUVendor();
+ if (cpu_vendor) {
+ info->cpu_info = *cpu_vendor;
+ info->cpu_info.append(" ");
+ }
+
+ char x86_info[36];
+ snprintf(x86_info, sizeof(x86_info), "family %u model %u stepping %u",
+ raw_system_info->processor_level,
+ raw_system_info->processor_revision >> 8,
+ raw_system_info->processor_revision & 0xff);
+ info->cpu_info.append(x86_info);
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_PPC: {
+ info->cpu = "ppc";
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_PPC64: {
+ info->cpu = "ppc64";
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_SPARC: {
+ info->cpu = "sparc";
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_ARM: {
+ info->cpu = "arm";
+ GetARMCpuInfo(raw_system_info, &info->cpu_info);
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_ARM64: {
+ info->cpu = "arm64";
+ break;
+ }
+
+ case MD_CPU_ARCHITECTURE_MIPS: {
+ info->cpu = "mips";
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_MIPS64: {
+ info->cpu = "mips64";
+ break;
+ }
+
+ default: {
+ // Assign the numeric architecture ID into the CPU string.
+ char cpu_string[7];
+ snprintf(cpu_string, sizeof(cpu_string), "0x%04x",
+ raw_system_info->processor_architecture);
+ info->cpu = cpu_string;
+ break;
+ }
+ }
+
+ info->cpu_count = raw_system_info->number_of_processors;
+
+ return true;
+}
+
+// static
+bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
+ assert(dump);
+ assert(info);
+
+ info->os.clear();
+ info->os_short.clear();
+ info->os_version.clear();
+
+ MinidumpSystemInfo *system_info;
+ const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
+ if (!raw_system_info)
+ return false;
+
+ info->os_short = system_info->GetOS();
+
+ switch (raw_system_info->platform_id) {
+ case MD_OS_WIN32_NT: {
+ info->os = "Windows NT";
+ break;
+ }
+
+ case MD_OS_WIN32_WINDOWS: {
+ info->os = "Windows";
+ break;
+ }
+
+ case MD_OS_MAC_OS_X: {
+ info->os = "Mac OS X";
+ break;
+ }
+
+ case MD_OS_IOS: {
+ info->os = "iOS";
+ break;
+ }
+
+ case MD_OS_LINUX: {
+ info->os = "Linux";
+ break;
+ }
+
+ case MD_OS_SOLARIS: {
+ info->os = "Solaris";
+ break;
+ }
+
+ case MD_OS_ANDROID: {
+ info->os = "Android";
+ break;
+ }
+
+ case MD_OS_PS3: {
+ info->os = "PS3";
+ break;
+ }
+
+ case MD_OS_NACL: {
+ info->os = "NaCl";
+ break;
+ }
+
+ default: {
+ // Assign the numeric platform ID into the OS string.
+ char os_string[11];
+ snprintf(os_string, sizeof(os_string), "0x%08x",
+ raw_system_info->platform_id);
+ info->os = os_string;
+ break;
+ }
+ }
+
+ char os_version_string[33];
+ snprintf(os_version_string, sizeof(os_version_string), "%u.%u.%u",
+ raw_system_info->major_version,
+ raw_system_info->minor_version,
+ raw_system_info->build_number);
+ info->os_version = os_version_string;
+
+ const string *csd_version = system_info->GetCSDVersion();
+ if (csd_version) {
+ info->os_version.append(" ");
+ info->os_version.append(*csd_version);
+ }
+
+ return true;
+}
+
+// static
+bool MinidumpProcessor::GetProcessCreateTime(Minidump* dump,
+ uint32_t* process_create_time) {
+ assert(dump);
+ assert(process_create_time);
+
+ *process_create_time = 0;
+
+ MinidumpMiscInfo* minidump_misc_info = dump->GetMiscInfo();
+ if (!minidump_misc_info) {
+ return false;
+ }
+
+ const MDRawMiscInfo* md_raw_misc_info = minidump_misc_info->misc_info();
+ if (!md_raw_misc_info) {
+ return false;
+ }
+
+ if (!(md_raw_misc_info->flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES)) {
+ return false;
+ }
+
+ *process_create_time = md_raw_misc_info->process_create_time;
+ return true;
+}
+
+// static
+string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
+ MinidumpException *exception = dump->GetException();
+ if (!exception)
+ return "";
+
+ const MDRawExceptionStream *raw_exception = exception->exception();
+ if (!raw_exception)
+ return "";
+
+ if (address)
+ *address = raw_exception->exception_record.exception_address;
+
+ // The reason value is OS-specific and possibly CPU-specific. Set up
+ // sensible numeric defaults for the reason string in case we can't
+ // map the codes to a string (because there's no system info, or because
+ // it's an unrecognized platform, or because it's an unrecognized code.)
+ char reason_string[24];
+ uint32_t exception_code = raw_exception->exception_record.exception_code;
+ uint32_t exception_flags = raw_exception->exception_record.exception_flags;
+ snprintf(reason_string, sizeof(reason_string), "0x%08x / 0x%08x",
+ exception_code, exception_flags);
+ string reason = reason_string;
+
+ const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, NULL);
+ if (!raw_system_info)
+ return reason;
+
+ switch (raw_system_info->platform_id) {
+ case MD_OS_MAC_OS_X:
+ case MD_OS_IOS: {
+ char flags_string[11];
+ snprintf(flags_string, sizeof(flags_string), "0x%08x", exception_flags);
+ switch (exception_code) {
+ case MD_EXCEPTION_MAC_BAD_ACCESS:
+ reason = "EXC_BAD_ACCESS / ";
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS:
+ reason.append("KERN_INVALID_ADDRESS");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE:
+ reason.append("KERN_PROTECTION_FAILURE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_NO_ACCESS:
+ reason.append("KERN_NO_ACCESS");
+ break;
+ case MD_EXCEPTION_CODE_MAC_MEMORY_FAILURE:
+ reason.append("KERN_MEMORY_FAILURE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_MEMORY_ERROR:
+ reason.append("KERN_MEMORY_ERROR");
+ break;
+ default:
+ // arm and ppc overlap
+ if (raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_ARM ||
+ raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_ARM64) {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
+ reason.append("EXC_ARM_DA_ALIGN");
+ break;
+ case MD_EXCEPTION_CODE_MAC_ARM_DA_DEBUG:
+ reason.append("EXC_ARM_DA_DEBUG");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ } else if (raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_PPC) {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ:
+ reason.append("EXC_PPC_VM_PROT_READ");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_BADSPACE:
+ reason.append("EXC_PPC_BADSPACE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_UNALIGNED:
+ reason.append("EXC_PPC_UNALIGNED");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ } else if (raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_X86 ||
+ raw_system_info->processor_architecture ==
+ MD_CPU_ARCHITECTURE_AMD64) {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT:
+ reason.append("EXC_I386_GPFLT");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ } else {
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ }
+ break;
+ }
+ break;
+ case MD_EXCEPTION_MAC_BAD_INSTRUCTION:
+ reason = "EXC_BAD_INSTRUCTION / ";
+ switch (raw_system_info->processor_architecture) {
+ case MD_CPU_ARCHITECTURE_ARM:
+ case MD_CPU_ARCHITECTURE_ARM64: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
+ reason.append("EXC_ARM_UNDEFINED");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_PPC: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_PPC_INVALID_SYSCALL:
+ reason.append("EXC_PPC_INVALID_SYSCALL");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_UNIMPLEMENTED_INSTRUCTION:
+ reason.append("EXC_PPC_UNIPL_INST");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_INSTRUCTION:
+ reason.append("EXC_PPC_PRIVINST");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_REGISTER:
+ reason.append("EXC_PPC_PRIVREG");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_TRACE:
+ reason.append("EXC_PPC_TRACE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_PERFORMANCE_MONITOR:
+ reason.append("EXC_PPC_PERFMON");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_AMD64:
+ case MD_CPU_ARCHITECTURE_X86: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_X86_INVALID_OPERATION:
+ reason.append("EXC_I386_INVOP");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_INVALID_TASK_STATE_SEGMENT:
+ reason.append("EXC_I386_INVTSSFLT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_SEGMENT_NOT_PRESENT:
+ reason.append("EXC_I386_SEGNPFLT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_STACK_FAULT:
+ reason.append("EXC_I386_STKFLT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT:
+ reason.append("EXC_I386_GPFLT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT:
+ reason.append("EXC_I386_ALIGNFLT");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ case MD_EXCEPTION_MAC_ARITHMETIC:
+ reason = "EXC_ARITHMETIC / ";
+ switch (raw_system_info->processor_architecture) {
+ case MD_CPU_ARCHITECTURE_PPC: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_PPC_OVERFLOW:
+ reason.append("EXC_PPC_OVERFLOW");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_ZERO_DIVIDE:
+ reason.append("EXC_PPC_ZERO_DIVIDE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_INEXACT:
+ reason.append("EXC_FLT_INEXACT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_ZERO_DIVIDE:
+ reason.append("EXC_PPC_FLT_ZERO_DIVIDE");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_UNDERFLOW:
+ reason.append("EXC_PPC_FLT_UNDERFLOW");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_OVERFLOW:
+ reason.append("EXC_PPC_FLT_OVERFLOW");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_NOT_A_NUMBER:
+ reason.append("EXC_PPC_FLT_NOT_A_NUMBER");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_NO_EMULATION:
+ reason.append("EXC_PPC_NOEMULATION");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_ALTIVEC_ASSIST:
+ reason.append("EXC_PPC_ALTIVECASSIST");
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_AMD64:
+ case MD_CPU_ARCHITECTURE_X86: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_X86_DIV:
+ reason.append("EXC_I386_DIV");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_INTO:
+ reason.append("EXC_I386_INTO");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_NOEXT:
+ reason.append("EXC_I386_NOEXT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_EXTOVR:
+ reason.append("EXC_I386_EXTOVR");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_EXTERR:
+ reason.append("EXC_I386_EXTERR");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_EMERR:
+ reason.append("EXC_I386_EMERR");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_BOUND:
+ reason.append("EXC_I386_BOUND");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_SSEEXTERR:
+ reason.append("EXC_I386_SSEEXTERR");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ case MD_EXCEPTION_MAC_EMULATION:
+ reason = "EXC_EMULATION / ";
+ reason.append(flags_string);
+ break;
+ case MD_EXCEPTION_MAC_SOFTWARE:
+ reason = "EXC_SOFTWARE / ";
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_ABORT:
+ reason.append("SIGABRT");
+ break;
+ case MD_EXCEPTION_CODE_MAC_NS_EXCEPTION:
+ reason.append("UNCAUGHT_NS_EXCEPTION");
+ break;
+ // These are ppc only but shouldn't be a problem as they're
+ // unused on x86
+ case MD_EXCEPTION_CODE_MAC_PPC_TRAP:
+ reason.append("EXC_PPC_TRAP");
+ break;
+ case MD_EXCEPTION_CODE_MAC_PPC_MIGRATE:
+ reason.append("EXC_PPC_MIGRATE");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ case MD_EXCEPTION_MAC_BREAKPOINT:
+ reason = "EXC_BREAKPOINT / ";
+ switch (raw_system_info->processor_architecture) {
+ case MD_CPU_ARCHITECTURE_ARM:
+ case MD_CPU_ARCHITECTURE_ARM64: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
+ reason.append("EXC_ARM_DA_ALIGN");
+ break;
+ case MD_EXCEPTION_CODE_MAC_ARM_DA_DEBUG:
+ reason.append("EXC_ARM_DA_DEBUG");
+ break;
+ case MD_EXCEPTION_CODE_MAC_ARM_BREAKPOINT:
+ reason.append("EXC_ARM_BREAKPOINT");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_PPC: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_PPC_BREAKPOINT:
+ reason.append("EXC_PPC_BREAKPOINT");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ case MD_CPU_ARCHITECTURE_AMD64:
+ case MD_CPU_ARCHITECTURE_X86: {
+ switch (exception_flags) {
+ case MD_EXCEPTION_CODE_MAC_X86_SGL:
+ reason.append("EXC_I386_SGL");
+ break;
+ case MD_EXCEPTION_CODE_MAC_X86_BPT:
+ reason.append("EXC_I386_BPT");
+ break;
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+ default:
+ reason.append(flags_string);
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ case MD_EXCEPTION_MAC_SYSCALL:
+ reason = "EXC_SYSCALL / ";
+ reason.append(flags_string);
+ break;
+ case MD_EXCEPTION_MAC_MACH_SYSCALL:
+ reason = "EXC_MACH_SYSCALL / ";
+ reason.append(flags_string);
+ break;
+ case MD_EXCEPTION_MAC_RPC_ALERT:
+ reason = "EXC_RPC_ALERT / ";
+ reason.append(flags_string);
+ break;
+ }
+ break;
+ }
+
+ case MD_OS_WIN32_NT:
+ case MD_OS_WIN32_WINDOWS: {
+ switch (exception_code) {
+ case MD_EXCEPTION_CODE_WIN_CONTROL_C:
+ reason = "DBG_CONTROL_C";
+ break;
+ case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION:
+ reason = "EXCEPTION_GUARD_PAGE";
+ break;
+ case MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT:
+ reason = "EXCEPTION_DATATYPE_MISALIGNMENT";
+ break;
+ case MD_EXCEPTION_CODE_WIN_BREAKPOINT:
+ reason = "EXCEPTION_BREAKPOINT";
+ break;
+ case MD_EXCEPTION_CODE_WIN_SINGLE_STEP:
+ reason = "EXCEPTION_SINGLE_STEP";
+ break;
+ case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION:
+ // For EXCEPTION_ACCESS_VIOLATION, Windows puts the address that
+ // caused the fault in exception_information[1].
+ // exception_information[0] is 0 if the violation was caused by
+ // an attempt to read data, 1 if it was an attempt to write data,
+ // and 8 if this was a data execution violation.
+ // This information is useful in addition to the code address, which
+ // will be present in the crash thread's instruction field anyway.
+ 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:
+ reason = "EXCEPTION_ACCESS_VIOLATION_READ";
+ break;
+ case MD_ACCESS_VIOLATION_WIN_WRITE:
+ reason = "EXCEPTION_ACCESS_VIOLATION_WRITE";
+ break;
+ case MD_ACCESS_VIOLATION_WIN_EXEC:
+ reason = "EXCEPTION_ACCESS_VIOLATION_EXEC";
+ break;
+ default:
+ reason = "EXCEPTION_ACCESS_VIOLATION";
+ break;
+ }
+ } else {
+ reason = "EXCEPTION_ACCESS_VIOLATION";
+ }
+ if (address &&
+ raw_exception->exception_record.number_parameters >= 2) {
+ *address =
+ raw_exception->exception_record.exception_information[1];
+ }
+ break;
+ case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR:
+ // For EXCEPTION_IN_PAGE_ERROR, Windows puts the address that
+ // caused the fault in exception_information[1].
+ // exception_information[0] is 0 if the violation was caused by
+ // an attempt to read data, 1 if it was an attempt to write data,
+ // and 8 if this was a data execution violation.
+ // exception_information[2] contains the underlying NTSTATUS code,
+ // which is the explanation for why this error occured.
+ // This information is useful in addition to the code address, which
+ // will be present in the crash thread's instruction field anyway.
+ if (raw_exception->exception_record.number_parameters >= 1) {
+ MDInPageErrorTypeWin av_type =
+ static_cast<MDInPageErrorTypeWin>
+ (raw_exception->exception_record.exception_information[0]);
+ switch (av_type) {
+ case MD_IN_PAGE_ERROR_WIN_READ:
+ reason = "EXCEPTION_IN_PAGE_ERROR_READ";
+ break;
+ case MD_IN_PAGE_ERROR_WIN_WRITE:
+ reason = "EXCEPTION_IN_PAGE_ERROR_WRITE";
+ break;
+ case MD_IN_PAGE_ERROR_WIN_EXEC:
+ reason = "EXCEPTION_IN_PAGE_ERROR_EXEC";
+ break;
+ default:
+ reason = "EXCEPTION_IN_PAGE_ERROR";
+ break;
+ }
+ } else {
+ reason = "EXCEPTION_IN_PAGE_ERROR";
+ }
+ if (address &&
+ raw_exception->exception_record.number_parameters >= 2) {
+ *address =
+ raw_exception->exception_record.exception_information[1];
+ }
+ if (raw_exception->exception_record.number_parameters >= 3) {
+ uint32_t ntstatus =
+ static_cast<uint32_t>
+ (raw_exception->exception_record.exception_information[2]);
+ reason.append(" / ");
+ reason.append(NTStatusToString(ntstatus));
+ }
+ break;
+ case MD_EXCEPTION_CODE_WIN_INVALID_HANDLE:
+ reason = "EXCEPTION_INVALID_HANDLE";
+ break;
+ case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION:
+ reason = "EXCEPTION_ILLEGAL_INSTRUCTION";
+ break;
+ case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION:
+ reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
+ break;
+ case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION:
+ reason = "EXCEPTION_INVALID_DISPOSITION";
+ break;
+ case MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED:
+ reason = "EXCEPTION_BOUNDS_EXCEEDED";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND:
+ reason = "EXCEPTION_FLT_DENORMAL_OPERAND";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO:
+ reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT:
+ reason = "EXCEPTION_FLT_INEXACT_RESULT";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION:
+ reason = "EXCEPTION_FLT_INVALID_OPERATION";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW:
+ reason = "EXCEPTION_FLT_OVERFLOW";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK:
+ reason = "EXCEPTION_FLT_STACK_CHECK";
+ break;
+ case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW:
+ reason = "EXCEPTION_FLT_UNDERFLOW";
+ break;
+ case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO:
+ reason = "EXCEPTION_INT_DIVIDE_BY_ZERO";
+ break;
+ case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW:
+ reason = "EXCEPTION_INT_OVERFLOW";
+ break;
+ case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION:
+ reason = "EXCEPTION_PRIV_INSTRUCTION";
+ break;
+ case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW:
+ reason = "EXCEPTION_STACK_OVERFLOW";
+ break;
+ case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK:
+ reason = "EXCEPTION_POSSIBLE_DEADLOCK";
+ break;
+ case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN:
+ reason = "EXCEPTION_STACK_BUFFER_OVERRUN";
+ break;
+ case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION:
+ reason = "EXCEPTION_HEAP_CORRUPTION";
+ break;
+ case MD_EXCEPTION_OUT_OF_MEMORY:
+ reason = "Out of Memory";
+ break;
+ case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION:
+ reason = "Unhandled C++ Exception";
+ break;
+ default:
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+
+ case MD_OS_ANDROID:
+ case MD_OS_LINUX: {
+ switch (exception_code) {
+ case MD_EXCEPTION_CODE_LIN_SIGHUP:
+ reason = "SIGHUP";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGINT:
+ reason = "SIGINT";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGQUIT:
+ reason = "SIGQUIT";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGILL:
+ reason = "SIGILL";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGTRAP:
+ reason = "SIGTRAP";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGABRT:
+ reason = "SIGABRT";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGBUS:
+ reason = "SIGBUS";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGFPE:
+ reason = "SIGFPE";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGKILL:
+ reason = "SIGKILL";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGUSR1:
+ reason = "SIGUSR1";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGSEGV:
+ reason = "SIGSEGV";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGUSR2:
+ reason = "SIGUSR2";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGPIPE:
+ reason = "SIGPIPE";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGALRM:
+ reason = "SIGALRM";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGTERM:
+ reason = "SIGTERM";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGSTKFLT:
+ reason = "SIGSTKFLT";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGCHLD:
+ reason = "SIGCHLD";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGCONT:
+ reason = "SIGCONT";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGSTOP:
+ reason = "SIGSTOP";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGTSTP:
+ reason = "SIGTSTP";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGTTIN:
+ reason = "SIGTTIN";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGTTOU:
+ reason = "SIGTTOU";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGURG:
+ reason = "SIGURG";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGXCPU:
+ reason = "SIGXCPU";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGXFSZ:
+ reason = "SIGXFSZ";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGVTALRM:
+ reason = "SIGVTALRM";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGPROF:
+ reason = "SIGPROF";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGWINCH:
+ reason = "SIGWINCH";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGIO:
+ reason = "SIGIO";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGPWR:
+ reason = "SIGPWR";
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGSYS:
+ reason = "SIGSYS";
+ break;
+ case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
+ reason = "DUMP_REQUESTED";
+ break;
+ default:
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+
+ case MD_OS_SOLARIS: {
+ switch (exception_code) {
+ case MD_EXCEPTION_CODE_SOL_SIGHUP:
+ reason = "SIGHUP";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGINT:
+ reason = "SIGINT";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGQUIT:
+ reason = "SIGQUIT";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGILL:
+ reason = "SIGILL";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTRAP:
+ reason = "SIGTRAP";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGIOT:
+ reason = "SIGIOT | SIGABRT";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGEMT:
+ reason = "SIGEMT";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGFPE:
+ reason = "SIGFPE";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGKILL:
+ reason = "SIGKILL";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGBUS:
+ reason = "SIGBUS";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGSEGV:
+ reason = "SIGSEGV";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGSYS:
+ reason = "SIGSYS";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGPIPE:
+ reason = "SIGPIPE";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGALRM:
+ reason = "SIGALRM";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTERM:
+ reason = "SIGTERM";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGUSR1:
+ reason = "SIGUSR1";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGUSR2:
+ reason = "SIGUSR2";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGCLD:
+ reason = "SIGCLD | SIGCHLD";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGPWR:
+ reason = "SIGPWR";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGWINCH:
+ reason = "SIGWINCH";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGURG:
+ reason = "SIGURG";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGPOLL:
+ reason = "SIGPOLL | SIGIO";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGSTOP:
+ reason = "SIGSTOP";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTSTP:
+ reason = "SIGTSTP";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGCONT:
+ reason = "SIGCONT";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTTIN:
+ reason = "SIGTTIN";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTTOU:
+ reason = "SIGTTOU";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGVTALRM:
+ reason = "SIGVTALRM";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGPROF:
+ reason = "SIGPROF";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGXCPU:
+ reason = "SIGXCPU";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGXFSZ:
+ reason = "SIGXFSZ";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGWAITING:
+ reason = "SIGWAITING";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGLWP:
+ reason = "SIGLWP";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGFREEZE:
+ reason = "SIGFREEZE";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGTHAW:
+ reason = "SIGTHAW";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGCANCEL:
+ reason = "SIGCANCEL";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGLOST:
+ reason = "SIGLOST";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGXRES:
+ reason = "SIGXRES";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGJVM1:
+ reason = "SIGJVM1";
+ break;
+ case MD_EXCEPTION_CODE_SOL_SIGJVM2:
+ reason = "SIGJVM2";
+ break;
+ default:
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ break;
+ }
+
+ case MD_OS_PS3: {
+ switch (exception_code) {
+ case MD_EXCEPTION_CODE_PS3_UNKNOWN:
+ reason = "UNKNOWN";
+ break;
+ case MD_EXCEPTION_CODE_PS3_TRAP_EXCEP:
+ reason = "TRAP_EXCEP";
+ break;
+ case MD_EXCEPTION_CODE_PS3_PRIV_INSTR:
+ reason = "PRIV_INSTR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_ILLEGAL_INSTR:
+ reason = "ILLEGAL_INSTR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_INSTR_STORAGE:
+ reason = "INSTR_STORAGE";
+ break;
+ case MD_EXCEPTION_CODE_PS3_INSTR_SEGMENT:
+ reason = "INSTR_SEGMENT";
+ break;
+ case MD_EXCEPTION_CODE_PS3_DATA_STORAGE:
+ reason = "DATA_STORAGE";
+ break;
+ case MD_EXCEPTION_CODE_PS3_DATA_SEGMENT:
+ reason = "DATA_SEGMENT";
+ break;
+ case MD_EXCEPTION_CODE_PS3_FLOAT_POINT:
+ reason = "FLOAT_POINT";
+ break;
+ case MD_EXCEPTION_CODE_PS3_DABR_MATCH:
+ reason = "DABR_MATCH";
+ break;
+ case MD_EXCEPTION_CODE_PS3_ALIGN_EXCEP:
+ reason = "ALIGN_EXCEP";
+ break;
+ case MD_EXCEPTION_CODE_PS3_MEMORY_ACCESS:
+ reason = "MEMORY_ACCESS";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_ALIGN:
+ reason = "COPRO_ALIGN";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_INVALID_COM:
+ reason = "COPRO_INVALID_COM";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_ERR:
+ reason = "COPRO_ERR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_FIR:
+ reason = "COPRO_FIR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_DATA_SEGMENT:
+ reason = "COPRO_DATA_SEGMENT";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_DATA_STORAGE:
+ reason = "COPRO_DATA_STORAGE";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_STOP_INSTR:
+ reason = "COPRO_STOP_INSTR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_HALT_INSTR:
+ reason = "COPRO_HALT_INSTR";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_HALTINST_UNKNOWN:
+ reason = "COPRO_HALTINSTR_UNKNOWN";
+ break;
+ case MD_EXCEPTION_CODE_PS3_COPRO_MEMORY_ACCESS:
+ reason = "COPRO_MEMORY_ACCESS";
+ break;
+ case MD_EXCEPTION_CODE_PS3_GRAPHIC:
+ reason = "GRAPHIC";
+ break;
+ default:
+ BPLOG(INFO) << "Unknown exception reason "<< reason;
+ break;
+ }
+ break;
+ }
+
+ default: {
+ BPLOG(INFO) << "Unknown exception reason " << reason;
+ break;
+ }
+ }
+
+ return reason;
+}
+
+// static
+string MinidumpProcessor::GetAssertion(Minidump *dump) {
+ MinidumpAssertion *assertion = dump->GetAssertion();
+ if (!assertion)
+ return "";
+
+ const MDRawAssertionInfo *raw_assertion = assertion->assertion();
+ if (!raw_assertion)
+ return "";
+
+ string assertion_string;
+ switch (raw_assertion->type) {
+ case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER:
+ assertion_string = "Invalid parameter passed to library function";
+ break;
+ case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL:
+ assertion_string = "Pure virtual function called";
+ break;
+ default: {
+ char assertion_type[32];
+ snprintf(assertion_type, sizeof(assertion_type),
+ "0x%08x", raw_assertion->type);
+ assertion_string = "Unknown assertion type ";
+ assertion_string += assertion_type;
+ break;
+ }
+ }
+
+ string expression = assertion->expression();
+ if (!expression.empty()) {
+ assertion_string.append(" " + expression);
+ }
+
+ string function = assertion->function();
+ if (!function.empty()) {
+ assertion_string.append(" in function " + function);
+ }
+
+ string file = assertion->file();
+ if (!file.empty()) {
+ assertion_string.append(", in file " + file);
+ }
+
+ if (raw_assertion->line != 0) {
+ char assertion_line[32];
+ snprintf(assertion_line, sizeof(assertion_line), "%u", raw_assertion->line);
+ assertion_string.append(" at line ");
+ assertion_string.append(assertion_line);
+ }
+
+ return assertion_string;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc
new file mode 100644
index 000000000..d43c1fc97
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc
@@ -0,0 +1,645 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Unit test for MinidumpProcessor. Uses a pre-generated minidump and
+// corresponding symbol file, and checks the stack frames for correctness.
+
+#include <stdlib.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <utility>
+
+#include "breakpad_googletest_includes.h"
+#include "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(GetMemoryList, MinidumpMemoryList*());
+};
+
+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::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))
+
+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 = string(getenv("srcdir") ? getenv("srcdir") : ".") +
+ "/src/processor/testdata/symbols/test_app.pdb/" +
+ module->debug_identifier() +
+ "/test_app.sym";
+ return FOUND;
+ }
+
+ return NOT_FOUND;
+}
+
+SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
+ const CodeModule *module,
+ const SystemInfo *system_info,
+ string *symbol_file,
+ string *symbol_data) {
+ SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info,
+ symbol_file);
+ if (s == FOUND) {
+ std::ifstream in(symbol_file->c_str());
+ std::getline(in, *symbol_data, 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, 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 = string(getenv("srcdir") ? getenv("srcdir") : ".") +
+ "/src/processor/testdata/minidump2.dmp";
+ ProcessState state;
+ EXPECT_CALL(supplier, GetCStringSymbolData(
+ Property(&google_breakpad::CodeModule::code_file,
+ "c:\\test_app.exe"),
+ _, _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
+ EXPECT_CALL(supplier, GetCStringSymbolData(
+ Property(&google_breakpad::CodeModule::code_file,
+ Ne("c:\\test_app.exe")),
+ _, _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
+ // 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 = string(getenv("srcdir") ? getenv("srcdir") : ".") +
+ "/src/processor/testdata/minidump2.dmp";
+
+ ProcessState state;
+ ASSERT_EQ(processor.Process(minidump_file, &state),
+ google_breakpad::PROCESS_OK);
+ ASSERT_EQ(state.system_info()->os, kSystemInfoOS);
+ ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort);
+ ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion);
+ ASSERT_EQ(state.system_info()->cpu, kSystemInfoCPU);
+ ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo);
+ ASSERT_TRUE(state.crashed());
+ ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION_WRITE");
+ ASSERT_EQ(state.crash_address(), 0x45U);
+ ASSERT_EQ(state.threads()->size(), size_t(1));
+ 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());
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc
new file mode 100644
index 000000000..8f83969fe
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing
+// the results, including stack traces.
+//
+// Author: Mark Mentovai
+
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#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 {
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpProcessor;
+using google_breakpad::ProcessState;
+using google_breakpad::SimpleSymbolSupplier;
+using google_breakpad::scoped_ptr;
+
+// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
+// non-empty, is the base directory of a symbol storage area, laid out in
+// the format required by SimpleSymbolSupplier. If such a storage area
+// is specified, it is made available for use by the MinidumpProcessor.
+//
+// Returns the value of MinidumpProcessor::Process. If processing succeeds,
+// prints identifying OS and CPU information from the minidump, crash
+// information if the minidump was produced as a result of a crash, and
+// call stacks for each thread contained in the minidump. All information
+// is printed to stdout.
+bool PrintMinidumpProcess(const string &minidump_file,
+ const std::vector<string> &symbol_paths,
+ bool machine_readable,
+ bool output_stack_contents) {
+ scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
+ if (!symbol_paths.empty()) {
+ // TODO(mmentovai): check existence of symbol_path if specified?
+ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
+ }
+
+ BasicSourceLineResolver resolver;
+ MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
+
+ // Process the minidump.
+ Minidump dump(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 (machine_readable) {
+ PrintProcessStateMachineReadable(process_state);
+ } else {
+ PrintProcessState(process_state, output_stack_contents, &resolver);
+ }
+
+ return true;
+}
+
+void usage(const char *program_name) {
+ fprintf(stderr, "usage: %s [-m|-s] <minidump-file> [symbol-path ...]\n"
+ " -m : Output in machine-readable format\n"
+ " -s : Output stack contents\n",
+ program_name);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ const char *minidump_file;
+ bool machine_readable = false;
+ bool output_stack_contents = false;
+ int symbol_path_arg;
+
+ if (strcmp(argv[1], "-m") == 0) {
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ machine_readable = true;
+ minidump_file = argv[2];
+ symbol_path_arg = 3;
+ } else if (strcmp(argv[1], "-s") == 0) {
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ output_stack_contents = true;
+ minidump_file = argv[2];
+ symbol_path_arg = 3;
+ } else {
+ minidump_file = argv[1];
+ symbol_path_arg = 2;
+ }
+
+ // extra arguments are symbol paths
+ std::vector<string> symbol_paths;
+ if (argc > symbol_path_arg) {
+ for (int argi = symbol_path_arg; argi < argc; ++argi)
+ symbol_paths.push_back(argv[argi]);
+ }
+
+ return PrintMinidumpProcess(minidump_file,
+ symbol_paths,
+ machine_readable,
+ output_stack_contents) ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_machine_readable_test b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_machine_readable_test
new file mode 100755
index 000000000..2aadb2412
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_machine_readable_test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (c) 2007, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/minidump_stackwalk -m $testdata_dir/minidump2.dmp \
+ $testdata_dir/symbols | \
+ tr -d '\015' | \
+ diff -u $testdata_dir/minidump2.stackwalk.machine_readable.out -
+exit $?
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_test b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_test
new file mode 100755
index 000000000..f97902791
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk_test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (c) 2006, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/minidump_stackwalk $testdata_dir/minidump2.dmp \
+ $testdata_dir/symbols | \
+ tr -d '\015' | \
+ diff -u $testdata_dir/minidump2.stackwalk.out -
+exit $?
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc
new file mode 100644
index 000000000..d29e9f4e5
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc
@@ -0,0 +1,1521 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Unit test for Minidump. Uses a pre-generated minidump and
+// verifies that certain streams are correct.
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/minidump.h"
+#include "processor/logging.h"
+#include "processor/synth_minidump.h"
+
+namespace {
+
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpMemoryInfo;
+using google_breakpad::MinidumpMemoryInfoList;
+using google_breakpad::MinidumpMemoryList;
+using google_breakpad::MinidumpMemoryRegion;
+using google_breakpad::MinidumpModule;
+using google_breakpad::MinidumpModuleList;
+using google_breakpad::MinidumpSystemInfo;
+using google_breakpad::MinidumpThread;
+using google_breakpad::MinidumpThreadList;
+using google_breakpad::SynthMinidump::Context;
+using google_breakpad::SynthMinidump::Dump;
+using google_breakpad::SynthMinidump::Exception;
+using google_breakpad::SynthMinidump::Memory;
+using google_breakpad::SynthMinidump::Module;
+using google_breakpad::SynthMinidump::Section;
+using google_breakpad::SynthMinidump::Stream;
+using google_breakpad::SynthMinidump::String;
+using google_breakpad::SynthMinidump::SystemInfo;
+using google_breakpad::SynthMinidump::Thread;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using std::ifstream;
+using std::istringstream;
+using std::vector;
+using ::testing::Return;
+
+class MinidumpTest : public ::testing::Test {
+public:
+ void SetUp() {
+ minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") +
+ "/src/processor/testdata/minidump2.dmp";
+ }
+ string minidump_file_;
+};
+
+TEST_F(MinidumpTest, TestMinidumpFromFile) {
+ Minidump minidump(minidump_file_);
+ ASSERT_EQ(minidump.path(), minidump_file_);
+ ASSERT_TRUE(minidump.Read());
+ const MDRawHeader* header = minidump.header();
+ ASSERT_NE(header, (MDRawHeader*)NULL);
+ ASSERT_EQ(header->signature, uint32_t(MD_HEADER_SIGNATURE));
+
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
+ ASSERT_TRUE(md_module != NULL);
+ ASSERT_EQ("c:\\test_app.exe", md_module->code_file());
+ ASSERT_EQ("c:\\test_app.pdb", md_module->debug_file());
+ ASSERT_EQ("45D35F6C2d000", md_module->code_identifier());
+ ASSERT_EQ("5A9832E5287241C1838ED98914E9B7FF1", md_module->debug_identifier());
+}
+
+TEST_F(MinidumpTest, TestMinidumpFromStream) {
+ // read minidump contents into memory, construct a stringstream around them
+ ifstream file_stream(minidump_file_.c_str(), std::ios::in);
+ ASSERT_TRUE(file_stream.good());
+ vector<char> bytes;
+ file_stream.seekg(0, std::ios_base::end);
+ ASSERT_TRUE(file_stream.good());
+ bytes.resize(file_stream.tellg());
+ file_stream.seekg(0, std::ios_base::beg);
+ ASSERT_TRUE(file_stream.good());
+ file_stream.read(&bytes[0], bytes.size());
+ ASSERT_TRUE(file_stream.good());
+ string str(&bytes[0], bytes.size());
+ istringstream stream(str);
+ ASSERT_TRUE(stream.good());
+
+ // now read minidump from stringstream
+ Minidump minidump(stream);
+ ASSERT_EQ(minidump.path(), "");
+ ASSERT_TRUE(minidump.Read());
+ const MDRawHeader* header = minidump.header();
+ ASSERT_NE(header, (MDRawHeader*)NULL);
+ ASSERT_EQ(header->signature, uint32_t(MD_HEADER_SIGNATURE));
+ //TODO: add more checks here
+}
+
+TEST(Dump, ReadBackEmpty) {
+ Dump dump(0);
+ dump.Finish();
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream stream(contents);
+ Minidump minidump(stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
+}
+
+TEST(Dump, ReadBackEmptyBigEndian) {
+ Dump big_minidump(0, kBigEndian);
+ big_minidump.Finish();
+ string contents;
+ ASSERT_TRUE(big_minidump.GetContents(&contents));
+ istringstream stream(contents);
+ Minidump minidump(stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
+}
+
+TEST(Dump, OneStream) {
+ Dump dump(0, kBigEndian);
+ Stream stream(dump, 0xfbb7fa2bU);
+ stream.Append("stream contents");
+ dump.Add(&stream);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);
+
+ uint32_t stream_length;
+ ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
+ ASSERT_EQ(15U, stream_length);
+ char stream_contents[15];
+ ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
+ EXPECT_EQ(string("stream contents"),
+ string(stream_contents, sizeof(stream_contents)));
+
+ EXPECT_FALSE(minidump.GetThreadList());
+ EXPECT_FALSE(minidump.GetModuleList());
+ EXPECT_FALSE(minidump.GetMemoryList());
+ EXPECT_FALSE(minidump.GetException());
+ EXPECT_FALSE(minidump.GetAssertion());
+ EXPECT_FALSE(minidump.GetSystemInfo());
+ EXPECT_FALSE(minidump.GetMiscInfo());
+ EXPECT_FALSE(minidump.GetBreakpadInfo());
+}
+
+TEST(Dump, OneMemory) {
+ Dump dump(0, kBigEndian);
+ Memory memory(dump, 0x309d68010bd21b2cULL);
+ memory.Append("memory contents");
+ dump.Add(&memory);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ((uint32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);
+
+ MinidumpMemoryList *memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(memory_list != NULL);
+ ASSERT_EQ(1U, memory_list->region_count());
+
+ MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
+ ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
+ ASSERT_EQ(15U, region1->GetSize());
+ const uint8_t *region1_bytes = region1->GetMemory();
+ ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
+}
+
+// One thread --- and its requisite entourage.
+TEST(Dump, OneThread) {
+ Dump dump(0, kLittleEndian);
+ Memory stack(dump, 0x2326a0fa);
+ stack.Append("stack for thread");
+
+ MDRawContextX86 raw_context;
+ const uint32_t kExpectedEIP = 0x6913f540;
+ raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
+ raw_context.edi = 0x3ecba80d;
+ raw_context.esi = 0x382583b9;
+ raw_context.ebx = 0x7fccc03f;
+ raw_context.edx = 0xf62f8ec2;
+ raw_context.ecx = 0x46a6a6a8;
+ raw_context.eax = 0x6a5025e2;
+ raw_context.ebp = 0xd9fabb4a;
+ raw_context.eip = kExpectedEIP;
+ raw_context.cs = 0xbffe6eda;
+ raw_context.eflags = 0xb2ce1e2d;
+ raw_context.esp = 0x659caaa4;
+ raw_context.ss = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Thread thread(dump, 0xa898f11b, stack, context,
+ 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
+
+ dump.Add(&stack);
+ dump.Add(&context);
+ dump.Add(&thread);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(md_memory_list != NULL);
+ ASSERT_EQ(1U, md_memory_list->region_count());
+
+ MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
+ ASSERT_EQ(0x2326a0faU, md_region->GetBase());
+ ASSERT_EQ(16U, md_region->GetSize());
+ const uint8_t *region_bytes = md_region->GetMemory();
+ ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);
+
+ MinidumpThreadList *thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(1U, thread_list->thread_count());
+
+ MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(md_thread != NULL);
+ uint32_t thread_id;
+ ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
+ ASSERT_EQ(0xa898f11bU, thread_id);
+ MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
+ ASSERT_TRUE(md_stack != NULL);
+ ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
+ ASSERT_EQ(16U, md_stack->GetSize());
+ const uint8_t *md_stack_bytes = md_stack->GetMemory();
+ ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);
+
+ MinidumpContext *md_context = md_thread->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
+
+ uint64_t eip;
+ ASSERT_TRUE(md_context->GetInstructionPointer(&eip));
+ EXPECT_EQ(kExpectedEIP, eip);
+
+ const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
+ (md_raw_context->context_flags
+ & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
+ EXPECT_EQ(0x3ecba80dU, raw_context.edi);
+ EXPECT_EQ(0x382583b9U, raw_context.esi);
+ EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
+ EXPECT_EQ(0x6a5025e2U, raw_context.eax);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
+ EXPECT_EQ(kExpectedEIP, raw_context.eip);
+ EXPECT_EQ(0xbffe6edaU, raw_context.cs);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
+ EXPECT_EQ(0x659caaa4U, raw_context.esp);
+ EXPECT_EQ(0x2e951ef7U, raw_context.ss);
+}
+
+TEST(Dump, ThreadMissingMemory) {
+ Dump dump(0, kLittleEndian);
+ Memory stack(dump, 0x2326a0fa);
+ // Stack has no contents.
+
+ MDRawContextX86 raw_context;
+ memset(&raw_context, 0, sizeof(raw_context));
+ raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
+ Context context(dump, raw_context);
+
+ Thread thread(dump, 0xa898f11b, stack, context,
+ 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
+
+ dump.Add(&stack);
+ dump.Add(&context);
+ dump.Add(&thread);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ // This should succeed even though the thread has no stack memory.
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(1U, thread_list->thread_count());
+
+ MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(md_thread != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
+ ASSERT_EQ(0xa898f11bU, thread_id);
+
+ MinidumpContext* md_context = md_thread->GetContext();
+ ASSERT_NE(reinterpret_cast<MinidumpContext*>(NULL), md_context);
+
+ MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
+ ASSERT_EQ(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
+}
+
+TEST(Dump, ThreadMissingContext) {
+ Dump dump(0, kLittleEndian);
+ Memory stack(dump, 0x2326a0fa);
+ stack.Append("stack for thread");
+
+ // Context is empty.
+ Context context(dump);
+
+ Thread thread(dump, 0xa898f11b, stack, context,
+ 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
+
+ dump.Add(&stack);
+ dump.Add(&context);
+ dump.Add(&thread);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ // This should succeed even though the thread has no stack memory.
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(1U, thread_list->thread_count());
+
+ MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(md_thread != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
+ ASSERT_EQ(0xa898f11bU, thread_id);
+ MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
+ ASSERT_NE(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
+
+ MinidumpContext* md_context = md_thread->GetContext();
+ ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context);
+}
+
+static const MDVSFixedFileInfo fixed_file_info = {
+ 0xb2fba33a, // signature
+ 0x33d7a728, // struct_version
+ 0x31afcb20, // file_version_hi
+ 0xe51cdab1, // file_version_lo
+ 0xd1ea6907, // product_version_hi
+ 0x03032857, // product_version_lo
+ 0x11bf71d7, // file_flags_mask
+ 0x5fb8cdbf, // file_flags
+ 0xe45d0d5d, // file_os
+ 0x107d9562, // file_type
+ 0x5a8844d4, // file_subtype
+ 0xa8d30b20, // file_date_hi
+ 0x651c3e4e // file_date_lo
+};
+
+TEST(Dump, OneModule) {
+ Dump dump(0, kBigEndian);
+ String module_name(dump, "single module");
+ Section cv_info(dump);
+ cv_info
+ .D32(MD_CVINFOPDB70_SIGNATURE) // signature
+ // signature, a MDGUID
+ .D32(0xabcd1234)
+ .D16(0xf00d)
+ .D16(0xbeef)
+ .Append("\x01\x02\x03\x04\x05\x06\x07\x08")
+ .D32(1) // age
+ .AppendCString("c:\\foo\\file.pdb"); // pdb_file_name
+
+ String csd_version(dump, "Windows 9000");
+ SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
+
+ Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
+ module_name,
+ 0xb1054d2a,
+ 0x34571371,
+ fixed_file_info, // from synth_minidump_unittest_data.h
+ &cv_info, nullptr);
+
+ dump.Add(&module);
+ dump.Add(&module_name);
+ dump.Add(&cv_info);
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(1);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ((uint32_t) MD_MODULE_LIST_STREAM, dir->stream_type);
+
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ ASSERT_EQ(1U, md_module_list->module_count());
+
+ const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
+ ASSERT_TRUE(md_module != NULL);
+ ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
+ ASSERT_EQ(0xada542bd, md_module->size());
+ ASSERT_EQ("single module", md_module->code_file());
+ ASSERT_EQ("c:\\foo\\file.pdb", md_module->debug_file());
+ // time_date_stamp and size_of_image concatenated
+ ASSERT_EQ("B1054D2Aada542bd", md_module->code_identifier());
+ ASSERT_EQ("ABCD1234F00DBEEF01020304050607081", md_module->debug_identifier());
+
+ const MDRawModule *md_raw_module = md_module->module();
+ ASSERT_TRUE(md_raw_module != NULL);
+ ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
+ ASSERT_EQ(0x34571371U, md_raw_module->checksum);
+ ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
+ sizeof(fixed_file_info)) == 0);
+}
+
+// Test that a module with a MDCVInfoELF CV record is handled properly.
+TEST(Dump, OneModuleCVELF) {
+ Dump dump(0, kLittleEndian);
+ String module_name(dump, "elf module");
+ Section cv_info(dump);
+ cv_info
+ .D32(MD_CVINFOELF_SIGNATURE) // signature
+ // build_id
+ .Append("\x5f\xa9\xcd\xb4\x10\x53\xdf\x1b\x86\xfa\xb7\x33\xb4\xdf"
+ "\x37\x38\xce\xa3\x4a\x87");
+
+ const MDRawSystemInfo linux_x86 = {
+ MD_CPU_ARCHITECTURE_X86, // processor_architecture
+ 6, // processor_level
+ 0xd08, // processor_revision
+ 1, // number_of_processors
+ 0, // product_type
+ 0, // major_version
+ 0, // minor_version
+ 0, // build_number
+ MD_OS_LINUX, // platform_id
+ 0xdeadbeef, // csd_version_rva
+ 0x100, // suite_mask
+ 0, // reserved2
+ { // cpu
+ { // x86_cpu_info
+ { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
+ 0x6d8, // version_information
+ 0xafe9fbff, // feature_information
+ 0xffffffff // amd_extended_cpu_features
+ }
+ }
+ };
+ String csd_version(dump, "Literally Linux");
+ SystemInfo system_info(dump, linux_x86, csd_version);
+
+ Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
+ module_name,
+ 0xb1054d2a,
+ 0x34571371,
+ fixed_file_info, // from synth_minidump_unittest_data.h
+ &cv_info, nullptr);
+
+ dump.Add(&module);
+ dump.Add(&module_name);
+ dump.Add(&cv_info);
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ ASSERT_EQ(1U, md_module_list->module_count());
+
+ const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
+ ASSERT_TRUE(md_module != NULL);
+ ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
+ ASSERT_EQ(0xada542bd, md_module->size());
+ ASSERT_EQ("elf module", md_module->code_file());
+ // debug_file == code_file
+ ASSERT_EQ("elf module", md_module->debug_file());
+ // just the build_id, directly
+ ASSERT_EQ("5fa9cdb41053df1b86fab733b4df3738cea34a87",
+ md_module->code_identifier());
+ // build_id truncted to GUID length and treated as such, with zero
+ // age appended
+ ASSERT_EQ("B4CDA95F53101BDF86FAB733B4DF37380", md_module->debug_identifier());
+
+ const MDRawModule *md_raw_module = md_module->module();
+ ASSERT_TRUE(md_raw_module != NULL);
+ ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
+ ASSERT_EQ(0x34571371U, md_raw_module->checksum);
+ ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
+ sizeof(fixed_file_info)) == 0);
+}
+
+// Test that a build_id that's shorter than a GUID is handled properly.
+TEST(Dump, CVELFShort) {
+ Dump dump(0, kLittleEndian);
+ String module_name(dump, "elf module");
+ Section cv_info(dump);
+ cv_info
+ .D32(MD_CVINFOELF_SIGNATURE) // signature
+ // build_id, shorter than a GUID
+ .Append("\x5f\xa9\xcd\xb4");
+
+ const MDRawSystemInfo linux_x86 = {
+ MD_CPU_ARCHITECTURE_X86, // processor_architecture
+ 6, // processor_level
+ 0xd08, // processor_revision
+ 1, // number_of_processors
+ 0, // product_type
+ 0, // major_version
+ 0, // minor_version
+ 0, // build_number
+ MD_OS_LINUX, // platform_id
+ 0xdeadbeef, // csd_version_rva
+ 0x100, // suite_mask
+ 0, // reserved2
+ { // cpu
+ { // x86_cpu_info
+ { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
+ 0x6d8, // version_information
+ 0xafe9fbff, // feature_information
+ 0xffffffff // amd_extended_cpu_features
+ }
+ }
+ };
+ String csd_version(dump, "Literally Linux");
+ SystemInfo system_info(dump, linux_x86, csd_version);
+
+ Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
+ module_name,
+ 0xb1054d2a,
+ 0x34571371,
+ fixed_file_info, // from synth_minidump_unittest_data.h
+ &cv_info, nullptr);
+
+ dump.Add(&module);
+ dump.Add(&module_name);
+ dump.Add(&cv_info);
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ ASSERT_EQ(1U, md_module_list->module_count());
+
+ const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
+ ASSERT_TRUE(md_module != NULL);
+ // just the build_id, directly
+ ASSERT_EQ("5fa9cdb4", md_module->code_identifier());
+ // build_id expanded to GUID length and treated as such, with zero
+ // age appended
+ ASSERT_EQ("B4CDA95F0000000000000000000000000", md_module->debug_identifier());
+}
+
+// Test that a build_id that's very long is handled properly.
+TEST(Dump, CVELFLong) {
+ Dump dump(0, kLittleEndian);
+ String module_name(dump, "elf module");
+ Section cv_info(dump);
+ cv_info
+ .D32(MD_CVINFOELF_SIGNATURE) // signature
+ // build_id, lots of bytes
+ .Append("\x5f\xa9\xcd\xb4\x10\x53\xdf\x1b\x86\xfa\xb7\x33\xb4\xdf"
+ "\x37\x38\xce\xa3\x4a\x87\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f");
+
+
+ const MDRawSystemInfo linux_x86 = {
+ MD_CPU_ARCHITECTURE_X86, // processor_architecture
+ 6, // processor_level
+ 0xd08, // processor_revision
+ 1, // number_of_processors
+ 0, // product_type
+ 0, // major_version
+ 0, // minor_version
+ 0, // build_number
+ MD_OS_LINUX, // platform_id
+ 0xdeadbeef, // csd_version_rva
+ 0x100, // suite_mask
+ 0, // reserved2
+ { // cpu
+ { // x86_cpu_info
+ { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
+ 0x6d8, // version_information
+ 0xafe9fbff, // feature_information
+ 0xffffffff // amd_extended_cpu_features
+ }
+ }
+ };
+ String csd_version(dump, "Literally Linux");
+ SystemInfo system_info(dump, linux_x86, csd_version);
+
+ Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
+ module_name,
+ 0xb1054d2a,
+ 0x34571371,
+ fixed_file_info, // from synth_minidump_unittest_data.h
+ &cv_info, nullptr);
+
+ dump.Add(&module);
+ dump.Add(&module_name);
+ dump.Add(&cv_info);
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ ASSERT_EQ(1U, md_module_list->module_count());
+
+ const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
+ ASSERT_TRUE(md_module != NULL);
+ // just the build_id, directly
+ ASSERT_EQ(
+ "5fa9cdb41053df1b86fab733b4df3738cea34a870102030405060708090a0b0c0d0e0f",
+ md_module->code_identifier());
+ // build_id truncated to GUID length and treated as such, with zero
+ // age appended.
+ ASSERT_EQ("B4CDA95F53101BDF86FAB733B4DF37380", md_module->debug_identifier());
+}
+
+TEST(Dump, OneSystemInfo) {
+ Dump dump(0, kLittleEndian);
+ String csd_version(dump, "Petulant Pierogi");
+ SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
+
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ((uint32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);
+
+ MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(md_system_info != NULL);
+ ASSERT_EQ("windows", md_system_info->GetOS());
+ ASSERT_EQ("x86", md_system_info->GetCPU());
+ ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
+ ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
+}
+
+TEST(Dump, BigDump) {
+ Dump dump(0, kLittleEndian);
+
+ // A SystemInfo stream.
+ String csd_version(dump, "Munificent Macaque");
+ SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
+ dump.Add(&csd_version);
+ dump.Add(&system_info);
+
+ // Five threads!
+ Memory stack0(dump, 0x70b9ebfc);
+ stack0.Append("stack for thread zero");
+ MDRawContextX86 raw_context0;
+ raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
+ raw_context0.eip = 0xaf0709e4;
+ Context context0(dump, raw_context0);
+ Thread thread0(dump, 0xbbef4432, stack0, context0,
+ 0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
+ dump.Add(&stack0);
+ dump.Add(&context0);
+ dump.Add(&thread0);
+
+ Memory stack1(dump, 0xf988cc45);
+ stack1.Append("stack for thread one");
+ MDRawContextX86 raw_context1;
+ raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
+ raw_context1.eip = 0xe4f56f81;
+ Context context1(dump, raw_context1);
+ Thread thread1(dump, 0x657c3f58, stack1, context1,
+ 0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
+ dump.Add(&stack1);
+ dump.Add(&context1);
+ dump.Add(&thread1);
+
+ Memory stack2(dump, 0xc8a92e7c);
+ stack2.Append("stack for thread two");
+ MDRawContextX86 raw_context2;
+ raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
+ raw_context2.eip = 0xb336a438;
+ Context context2(dump, raw_context2);
+ Thread thread2(dump, 0xdf4b8a71, stack2, context2,
+ 0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
+ dump.Add(&stack2);
+ dump.Add(&context2);
+ dump.Add(&thread2);
+
+ Memory stack3(dump, 0x36d08e08);
+ stack3.Append("stack for thread three");
+ MDRawContextX86 raw_context3;
+ raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
+ raw_context3.eip = 0xdf99a60c;
+ Context context3(dump, raw_context3);
+ Thread thread3(dump, 0x86e6c341, stack3, context3,
+ 0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
+ dump.Add(&stack3);
+ dump.Add(&context3);
+ dump.Add(&thread3);
+
+ Memory stack4(dump, 0x1e0ab4fa);
+ stack4.Append("stack for thread four");
+ MDRawContextX86 raw_context4;
+ raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
+ raw_context4.eip = 0xaa646267;
+ Context context4(dump, raw_context4);
+ Thread thread4(dump, 0x261a28d4, stack4, context4,
+ 0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
+ dump.Add(&stack4);
+ dump.Add(&context4);
+ dump.Add(&thread4);
+
+ // Three modules!
+ String module1_name(dump, "module one");
+ Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
+ dump.Add(&module1_name);
+ dump.Add(&module1);
+
+ String module2_name(dump, "module two");
+ Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
+ dump.Add(&module2_name);
+ dump.Add(&module2);
+
+ String module3_name(dump, "module three");
+ Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
+ dump.Add(&module3_name);
+ dump.Add(&module3);
+
+ // Add one more memory region, on top of the five stacks.
+ Memory memory5(dump, 0x61979e828040e564ULL);
+ memory5.Append("contents of memory 5");
+ dump.Add(&memory5);
+
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());
+
+ // Check the threads.
+ MinidumpThreadList *thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(5U, thread_list->thread_count());
+ uint32_t thread_id;
+ ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
+ ASSERT_EQ(0xbbef4432U, thread_id);
+ ASSERT_EQ(0x70b9ebfcU,
+ thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
+ ASSERT_EQ(0xaf0709e4U,
+ thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
+ ->eip);
+
+ ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
+ ASSERT_EQ(0x657c3f58U, thread_id);
+ ASSERT_EQ(0xf988cc45U,
+ thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
+ ASSERT_EQ(0xe4f56f81U,
+ thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
+ ->eip);
+
+ ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
+ ASSERT_EQ(0xdf4b8a71U, thread_id);
+ ASSERT_EQ(0xc8a92e7cU,
+ thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
+ ASSERT_EQ(0xb336a438U,
+ thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
+ ->eip);
+
+ ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
+ ASSERT_EQ(0x86e6c341U, thread_id);
+ ASSERT_EQ(0x36d08e08U,
+ thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
+ ASSERT_EQ(0xdf99a60cU,
+ thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
+ ->eip);
+
+ ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
+ ASSERT_EQ(0x261a28d4U, thread_id);
+ ASSERT_EQ(0x1e0ab4faU,
+ thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
+ ASSERT_EQ(0xaa646267U,
+ thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
+ ->eip);
+
+ // Check the modules.
+ MinidumpModuleList *md_module_list = minidump.GetModuleList();
+ ASSERT_TRUE(md_module_list != NULL);
+ ASSERT_EQ(3U, md_module_list->module_count());
+ EXPECT_EQ(0xeb77da57b5d4cbdaULL,
+ md_module_list->GetModuleAtIndex(0)->base_address());
+ EXPECT_EQ(0x8675884adfe5ac90ULL,
+ md_module_list->GetModuleAtIndex(1)->base_address());
+ EXPECT_EQ(0x95fc1544da321b6cULL,
+ md_module_list->GetModuleAtIndex(2)->base_address());
+}
+
+TEST(Dump, OneMemoryInfo) {
+ Dump dump(0, kBigEndian);
+ Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM);
+
+ // Add the MDRawMemoryInfoList header.
+ const uint64_t kNumberOfEntries = 1;
+ stream.D32(sizeof(MDRawMemoryInfoList)) // size_of_header
+ .D32(sizeof(MDRawMemoryInfo)) // size_of_entry
+ .D64(kNumberOfEntries); // number_of_entries
+
+
+ // Now add a MDRawMemoryInfo entry.
+ const uint64_t kBaseAddress = 0x1000;
+ const uint64_t kRegionSize = 0x2000;
+ stream.D64(kBaseAddress) // base_address
+ .D64(kBaseAddress) // allocation_base
+ .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // allocation_protection
+ .D32(0) // __alignment1
+ .D64(kRegionSize) // region_size
+ .D32(MD_MEMORY_STATE_COMMIT) // state
+ .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // protection
+ .D32(MD_MEMORY_TYPE_PRIVATE) // type
+ .D32(0); // __alignment2
+
+ dump.Add(&stream);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ((uint32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type);
+
+ MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList();
+ ASSERT_TRUE(info_list != NULL);
+ ASSERT_EQ(1U, info_list->info_count());
+
+ const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0);
+ ASSERT_EQ(kBaseAddress, info1->GetBase());
+ ASSERT_EQ(kRegionSize, info1->GetSize());
+ ASSERT_TRUE(info1->IsExecutable());
+ ASSERT_TRUE(info1->IsWritable());
+
+ // Should get back the same memory region here.
+ const MinidumpMemoryInfo *info2 =
+ info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2);
+ ASSERT_EQ(kBaseAddress, info2->GetBase());
+ ASSERT_EQ(kRegionSize, info2->GetSize());
+}
+
+TEST(Dump, OneExceptionX86) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextX86 raw_context;
+ raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
+ raw_context.edi = 0x3ecba80d;
+ raw_context.esi = 0x382583b9;
+ raw_context.ebx = 0x7fccc03f;
+ raw_context.edx = 0xf62f8ec2;
+ raw_context.ecx = 0x46a6a6a8;
+ raw_context.eax = 0x6a5025e2;
+ raw_context.ebp = 0xd9fabb4a;
+ raw_context.eip = 0x6913f540;
+ raw_context.cs = 0xbffe6eda;
+ raw_context.eflags = 0xb2ce1e2d;
+ raw_context.esp = 0x659caaa4;
+ raw_context.ss = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
+ const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
+ (md_raw_context->context_flags
+ & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
+ EXPECT_EQ(0x3ecba80dU, raw_context.edi);
+ EXPECT_EQ(0x382583b9U, raw_context.esi);
+ EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
+ EXPECT_EQ(0x6a5025e2U, raw_context.eax);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
+ EXPECT_EQ(0x6913f540U, raw_context.eip);
+ EXPECT_EQ(0xbffe6edaU, raw_context.cs);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
+ EXPECT_EQ(0x659caaa4U, raw_context.esp);
+ EXPECT_EQ(0x2e951ef7U, raw_context.ss);
+}
+
+TEST(Dump, OneExceptionX86XState) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextX86 raw_context;
+ raw_context.context_flags = MD_CONTEXT_X86_INTEGER |
+ MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_XSTATE;
+ raw_context.edi = 0x3ecba80d;
+ raw_context.esi = 0x382583b9;
+ raw_context.ebx = 0x7fccc03f;
+ raw_context.edx = 0xf62f8ec2;
+ raw_context.ecx = 0x46a6a6a8;
+ raw_context.eax = 0x6a5025e2;
+ raw_context.ebp = 0xd9fabb4a;
+ raw_context.eip = 0x6913f540;
+ raw_context.cs = 0xbffe6eda;
+ raw_context.eflags = 0xb2ce1e2d;
+ raw_context.esp = 0x659caaa4;
+ raw_context.ss = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
+ const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
+ (md_raw_context->context_flags
+ & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
+ EXPECT_EQ(0x3ecba80dU, raw_context.edi);
+ EXPECT_EQ(0x382583b9U, raw_context.esi);
+ EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
+ EXPECT_EQ(0x6a5025e2U, raw_context.eax);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
+ EXPECT_EQ(0x6913f540U, raw_context.eip);
+ EXPECT_EQ(0xbffe6edaU, raw_context.cs);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
+ EXPECT_EQ(0x659caaa4U, raw_context.esp);
+ EXPECT_EQ(0x2e951ef7U, raw_context.ss);
+}
+
+// Testing that the CPU type can be loaded from a system info stream when
+// the CPU flags are missing from the context_flags of an exception record
+TEST(Dump, OneExceptionX86NoCPUFlags) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextX86 raw_context;
+ // Intentionally not setting CPU type in the context_flags
+ raw_context.context_flags = 0;
+ raw_context.edi = 0x3ecba80d;
+ raw_context.esi = 0x382583b9;
+ raw_context.ebx = 0x7fccc03f;
+ raw_context.edx = 0xf62f8ec2;
+ raw_context.ecx = 0x46a6a6a8;
+ raw_context.eax = 0x6a5025e2;
+ raw_context.ebp = 0xd9fabb4a;
+ raw_context.eip = 0x6913f540;
+ raw_context.cs = 0xbffe6eda;
+ raw_context.eflags = 0xb2ce1e2d;
+ raw_context.esp = 0x659caaa4;
+ raw_context.ss = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+
+ // Add system info. This is needed as an alternative source for CPU type
+ // information. Note, that the CPU flags were intentionally skipped from
+ // the context_flags and this alternative source is required.
+ String csd_version(dump, "Service Pack 2");
+ SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
+ dump.Add(&system_info);
+ dump.Add(&csd_version);
+
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+
+ ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
+ const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
+ ASSERT_TRUE(md_raw_context != NULL);
+
+ // Even though the CPU flags were missing from the context_flags, the
+ // GetContext call above is expected to load the missing CPU flags from the
+ // system info stream and set the CPU type bits in context_flags.
+ ASSERT_EQ((uint32_t) (MD_CONTEXT_X86), md_raw_context->context_flags);
+
+ EXPECT_EQ(0x3ecba80dU, raw_context.edi);
+ EXPECT_EQ(0x382583b9U, raw_context.esi);
+ EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
+ EXPECT_EQ(0x6a5025e2U, raw_context.eax);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
+ EXPECT_EQ(0x6913f540U, raw_context.eip);
+ EXPECT_EQ(0xbffe6edaU, raw_context.cs);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
+ EXPECT_EQ(0x659caaa4U, raw_context.esp);
+ EXPECT_EQ(0x2e951ef7U, raw_context.ss);
+}
+
+// This test covers a scenario where a dump contains an exception but the
+// context record of the exception is missing the CPU type information in its
+// context_flags. The dump has no system info stream so it is imposible to
+// deduce the CPU type, hence the context record is unusable.
+TEST(Dump, OneExceptionX86NoCPUFlagsNoSystemInfo) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextX86 raw_context;
+ // Intentionally not setting CPU type in the context_flags
+ raw_context.context_flags = 0;
+ raw_context.edi = 0x3ecba80d;
+ raw_context.esi = 0x382583b9;
+ raw_context.ebx = 0x7fccc03f;
+ raw_context.edx = 0xf62f8ec2;
+ raw_context.ecx = 0x46a6a6a8;
+ raw_context.eax = 0x6a5025e2;
+ raw_context.ebp = 0xd9fabb4a;
+ raw_context.eip = 0x6913f540;
+ raw_context.cs = 0xbffe6eda;
+ raw_context.eflags = 0xb2ce1e2d;
+ raw_context.esp = 0x659caaa4;
+ raw_context.ss = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ // The context record of the exception is unusable because the context_flags
+ // don't have CPU type information and at the same time the minidump lacks
+ // system info stream so it is impossible to deduce the CPU type.
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_EQ(NULL, md_context);
+}
+
+TEST(Dump, OneExceptionARM) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextARM raw_context;
+ raw_context.context_flags = MD_CONTEXT_ARM_INTEGER;
+ raw_context.iregs[0] = 0x3ecba80d;
+ raw_context.iregs[1] = 0x382583b9;
+ raw_context.iregs[2] = 0x7fccc03f;
+ raw_context.iregs[3] = 0xf62f8ec2;
+ raw_context.iregs[4] = 0x46a6a6a8;
+ raw_context.iregs[5] = 0x6a5025e2;
+ raw_context.iregs[6] = 0xd9fabb4a;
+ raw_context.iregs[7] = 0x6913f540;
+ raw_context.iregs[8] = 0xbffe6eda;
+ raw_context.iregs[9] = 0xb2ce1e2d;
+ raw_context.iregs[10] = 0x659caaa4;
+ raw_context.iregs[11] = 0xf0e0d0c0;
+ raw_context.iregs[12] = 0xa9b8c7d6;
+ raw_context.iregs[13] = 0x12345678;
+ raw_context.iregs[14] = 0xabcd1234;
+ raw_context.iregs[15] = 0x10203040;
+ raw_context.cpsr = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
+ const MDRawContextARM *md_raw_context = md_context->GetContextARM();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_ARM_INTEGER,
+ (md_raw_context->context_flags
+ & MD_CONTEXT_ARM_INTEGER));
+ EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
+ EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
+ EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
+ EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
+ EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
+ EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
+ EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
+ EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
+ EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
+ EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
+ EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
+ EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
+ EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
+}
+
+TEST(Dump, OneExceptionARMOldFlags) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextARM raw_context;
+ // MD_CONTEXT_ARM_INTEGER, but with _OLD
+ raw_context.context_flags = MD_CONTEXT_ARM_OLD | 0x00000002;
+ raw_context.iregs[0] = 0x3ecba80d;
+ raw_context.iregs[1] = 0x382583b9;
+ raw_context.iregs[2] = 0x7fccc03f;
+ raw_context.iregs[3] = 0xf62f8ec2;
+ raw_context.iregs[4] = 0x46a6a6a8;
+ raw_context.iregs[5] = 0x6a5025e2;
+ raw_context.iregs[6] = 0xd9fabb4a;
+ raw_context.iregs[7] = 0x6913f540;
+ raw_context.iregs[8] = 0xbffe6eda;
+ raw_context.iregs[9] = 0xb2ce1e2d;
+ raw_context.iregs[10] = 0x659caaa4;
+ raw_context.iregs[11] = 0xf0e0d0c0;
+ raw_context.iregs[12] = 0xa9b8c7d6;
+ raw_context.iregs[13] = 0x12345678;
+ raw_context.iregs[14] = 0xabcd1234;
+ raw_context.iregs[15] = 0x10203040;
+ raw_context.cpsr = 0x2e951ef7;
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext *md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
+ const MDRawContextARM *md_raw_context = md_context->GetContextARM();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_ARM_INTEGER,
+ (md_raw_context->context_flags
+ & MD_CONTEXT_ARM_INTEGER));
+ EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
+ EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
+ EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
+ EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
+ EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
+ EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
+ EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
+ EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
+ EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
+ EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
+ EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
+ EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
+ EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
+}
+
+TEST(Dump, OneExceptionMIPS) {
+ Dump dump(0, kLittleEndian);
+
+ MDRawContextMIPS raw_context;
+ raw_context.context_flags = MD_CONTEXT_MIPS_INTEGER;
+ raw_context.iregs[0] = 0x3ecba80d;
+ raw_context.iregs[1] = 0x382583b9;
+ raw_context.iregs[2] = 0x7fccc03f;
+ raw_context.iregs[3] = 0xf62f8ec2;
+ raw_context.iregs[4] = 0x46a6a6a8;
+ raw_context.iregs[5] = 0x6a5025e2;
+ raw_context.iregs[6] = 0xd9fabb4a;
+ raw_context.iregs[7] = 0x6913f540;
+ raw_context.iregs[8] = 0xbffe6eda;
+ raw_context.iregs[9] = 0xb2ce1e2d;
+ raw_context.iregs[10] = 0x659caaa4;
+ raw_context.iregs[11] = 0xf0e0d0c0;
+ raw_context.iregs[12] = 0xa9b8c7d6;
+ raw_context.iregs[13] = 0x12345678;
+ raw_context.iregs[14] = 0xabcd1234;
+ raw_context.iregs[15] = 0x10203040;
+ raw_context.iregs[16] = 0xa80d3ecb;
+ raw_context.iregs[17] = 0x83b93825;
+ raw_context.iregs[18] = 0xc03f7fcc;
+ raw_context.iregs[19] = 0x8ec2f62f;
+ raw_context.iregs[20] = 0xa6a846a6;
+ raw_context.iregs[21] = 0x25e26a50;
+ raw_context.iregs[22] = 0xbb4ad9fa;
+ raw_context.iregs[23] = 0xf5406913;
+ raw_context.iregs[24] = 0x6edabffe;
+ raw_context.iregs[25] = 0x1e2db2ce;
+ raw_context.iregs[26] = 0xaaa4659c;
+ raw_context.iregs[27] = 0xd0c0f0e0;
+ raw_context.iregs[28] = 0xc7d6a9b8;
+ raw_context.iregs[29] = 0x56781234;
+ raw_context.iregs[30] = 0x1234abcd;
+ raw_context.iregs[31] = 0x30401020;
+
+ Context context(dump, raw_context);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // Thread id.
+ 0xdcba4321, // Exception code.
+ 0xf0e0d0c0, // Exception flags.
+ 0x0919a9b9); // Exception address.
+
+ dump.Add(&context);
+ dump.Add(&exception);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ MinidumpException *md_exception = minidump.GetException();
+ ASSERT_TRUE(md_exception != NULL);
+
+ uint32_t thread_id;
+ ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
+ ASSERT_EQ(0x1234abcdU, thread_id);
+
+ const MDRawExceptionStream* raw_exception = md_exception->exception();
+ ASSERT_TRUE(raw_exception != NULL);
+ EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
+ EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
+ EXPECT_EQ(0x0919a9b9U,
+ raw_exception->exception_record.exception_address);
+
+ MinidumpContext* md_context = md_exception->GetContext();
+ ASSERT_TRUE(md_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_MIPS, md_context->GetContextCPU());
+ const MDRawContextMIPS* md_raw_context = md_context->GetContextMIPS();
+ ASSERT_TRUE(md_raw_context != NULL);
+ ASSERT_EQ((uint32_t) MD_CONTEXT_MIPS_INTEGER,
+ (md_raw_context->context_flags & MD_CONTEXT_MIPS_INTEGER));
+ EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
+ EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
+ EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
+ EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
+ EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
+ EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
+ EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
+ EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
+ EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
+ EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
+ EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
+ EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
+ EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
+ EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
+ EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
+ EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
+ EXPECT_EQ(0xa80d3ecbU, raw_context.iregs[16]);
+ EXPECT_EQ(0x83b93825U, raw_context.iregs[17]);
+ EXPECT_EQ(0xc03f7fccU, raw_context.iregs[18]);
+ EXPECT_EQ(0x8ec2f62fU, raw_context.iregs[19]);
+ EXPECT_EQ(0xa6a846a6U, raw_context.iregs[20]);
+ EXPECT_EQ(0x25e26a50U, raw_context.iregs[21]);
+ EXPECT_EQ(0xbb4ad9faU, raw_context.iregs[22]);
+ EXPECT_EQ(0xf5406913U, raw_context.iregs[23]);
+ EXPECT_EQ(0x6edabffeU, raw_context.iregs[24]);
+ EXPECT_EQ(0x1e2db2ceU, raw_context.iregs[25]);
+ EXPECT_EQ(0xaaa4659cU, raw_context.iregs[26]);
+ EXPECT_EQ(0xd0c0f0e0U, raw_context.iregs[27]);
+ EXPECT_EQ(0xc7d6a9b8U, raw_context.iregs[28]);
+ EXPECT_EQ(0x56781234U, raw_context.iregs[29]);
+ EXPECT_EQ(0x1234abcdU, raw_context.iregs[30]);
+ EXPECT_EQ(0x30401020U, raw_context.iregs[31]);
+}
+
+} // namespace
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.cc b/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.cc
new file mode 100644
index 000000000..025ab883a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// module_comparer.cc: ModuleComparer implementation.
+// See module_comparer.h for documentation.
+//
+// Author: lambxsy@google.com (Siyang Xie)
+
+#include "processor/module_comparer.h"
+
+#include <map>
+#include <string>
+
+#include "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.
+ unsigned int serialized_size = 0;
+ scoped_array<char> serialized_data(
+ serializer_.Serialize(*(basic_module.get()), &serialized_size));
+ ASSERT_TRUE(serialized_data.get());
+ BPLOG(INFO) << "Serialized size = " << serialized_size << " Bytes";
+
+ // Load FastSourceLineResolver::Module using serialized data.
+ ASSERT_TRUE(fast_module->LoadMapFromMemory(serialized_data.get(),
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.h b/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.h
new file mode 100644
index 000000000..fcbd51775
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/module_comparer.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// module_comparer.h: ModuleComparer reads a string format of symbol file, and
+// loads the symbol into both BasicSourceLineResolver::Module and
+// FastSourceLineResolve::Module. It then traverses both Modules and compare
+// the content of data to verify the correctness of new fast module.
+// ModuleCompare class is a tool to verify correctness of a loaded
+// FastSourceLineResolver::Module instance, i.e., in-memory representation of
+// parsed symbol. ModuleComparer class should be used for testing purpose only,
+// e.g., in fast_source_line_resolver_unittest.
+//
+// Author: lambxsy@google.com (Siyang Xie)
+
+#ifndef PROCESSOR_MODULE_COMPARER_H__
+#define PROCESSOR_MODULE_COMPARER_H__
+
+#include <string>
+
+#include "processor/basic_source_line_resolver_types.h"
+#include "processor/fast_source_line_resolver_types.h"
+#include "processor/module_serializer.h"
+#include "processor/windows_frame_info.h"
+
+namespace google_breakpad {
+
+class ModuleComparer {
+ public:
+ ModuleComparer(): fast_resolver_(new FastSourceLineResolver),
+ basic_resolver_(new BasicSourceLineResolver) { }
+ ~ModuleComparer() {
+ delete fast_resolver_;
+ delete basic_resolver_;
+ }
+
+ // BasicSourceLineResolver loads its module using the symbol data,
+ // ModuleSerializer serialize the loaded module into a memory chunk,
+ // FastSourceLineResolver loads its module using the serialized memory chunk,
+ // Then, traverse both modules together and compare underlying data
+ // return true if both modules contain exactly same data.
+ bool Compare(const string &symbol_data);
+
+ private:
+ typedef BasicSourceLineResolver::Module BasicModule;
+ typedef FastSourceLineResolver::Module FastModule;
+ typedef BasicSourceLineResolver::Function BasicFunc;
+ typedef FastSourceLineResolver::Function FastFunc;
+ typedef BasicSourceLineResolver::Line BasicLine;
+ typedef FastSourceLineResolver::Line FastLine;
+ typedef BasicSourceLineResolver::PublicSymbol BasicPubSymbol;
+ typedef FastSourceLineResolver::PublicSymbol FastPubSymbol;
+ typedef WindowsFrameInfo WFI;
+
+ bool CompareModule(const BasicModule *oldmodule,
+ const FastModule *newmodule) const;
+ bool CompareFunction(const BasicFunc *oldfunc, const FastFunc *newfunc) const;
+ bool CompareLine(const BasicLine *oldline, const FastLine *newline) const;
+ bool ComparePubSymbol(const BasicPubSymbol*, const FastPubSymbol*) const;
+ bool CompareWFI(const WindowsFrameInfo&, const WindowsFrameInfo&) const;
+
+ // Compare ContainedRangeMap
+ bool CompareCRM(const ContainedRangeMap<MemAddr, linked_ptr<WFI> >*,
+ const StaticContainedRangeMap<MemAddr, char>*) const;
+
+ FastSourceLineResolver *fast_resolver_;
+ BasicSourceLineResolver *basic_resolver_;
+ ModuleSerializer serializer_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_MODULE_COMPARER_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/module_factory.h b/toolkit/crashreporter/google-breakpad/src/processor/module_factory.h
new file mode 100644
index 000000000..7aa7caa59
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/module_factory.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// module_factory.h: ModuleFactory a factory that provides
+// an interface for creating a Module and deferring instantiation to subclasses
+// BasicModuleFactory and FastModuleFactory.
+
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_MODULE_FACTORY_H__
+#define PROCESSOR_MODULE_FACTORY_H__
+
+#include "processor/basic_source_line_resolver_types.h"
+#include "processor/fast_source_line_resolver_types.h"
+#include "processor/source_line_resolver_base_types.h"
+
+namespace google_breakpad {
+
+class ModuleFactory {
+ public:
+ virtual ~ModuleFactory() { };
+ virtual SourceLineResolverBase::Module* CreateModule(
+ const string &name) const = 0;
+};
+
+class BasicModuleFactory : public ModuleFactory {
+ public:
+ virtual ~BasicModuleFactory() { }
+ virtual BasicSourceLineResolver::Module* CreateModule(
+ const string &name) const {
+ return new BasicSourceLineResolver::Module(name);
+ }
+};
+
+class FastModuleFactory : public ModuleFactory {
+ public:
+ virtual ~FastModuleFactory() { }
+ virtual FastSourceLineResolver::Module* CreateModule(
+ const string &name) const {
+ return new FastSourceLineResolver::Module(name);
+ }
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_MODULE_FACTORY_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.cc b/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.cc
new file mode 100644
index 000000000..6ac60c1fc
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// module_serializer.cc: ModuleSerializer implementation.
+//
+// See module_serializer.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include "processor/module_serializer.h"
+
+#include <map>
+#include <string>
+
+#include "processor/basic_code_module.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+// Definition of static member variable in SimplerSerializer<Funcion>, which
+// is declared in file "simple_serializer-inl.h"
+RangeMapSerializer< MemAddr, linked_ptr<BasicSourceLineResolver::Line> >
+SimpleSerializer<BasicSourceLineResolver::Function>::range_map_serializer_;
+
+size_t ModuleSerializer::SizeOf(const BasicSourceLineResolver::Module &module) {
+ size_t total_size_alloc_ = 0;
+
+ // 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_);
+
+ // 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);
+ // Write a null terminator.
+ dest = SimpleSerializer<char>::Write(0, dest);
+ return dest;
+}
+
+char* ModuleSerializer::Serialize(
+ const BasicSourceLineResolver::Module &module, unsigned int *size) {
+ // Compute size of memory to allocate.
+ unsigned int size_to_alloc = SizeOf(module);
+
+ // Allocate memory for serialized data.
+ char *serialized_data = new char[size_to_alloc];
+ if (!serialized_data) {
+ BPLOG(ERROR) << "ModuleSerializer: memory allocation failed, "
+ << "size to alloc: " << size_to_alloc;
+ if (size) *size = 0;
+ return NULL;
+ }
+
+ // Write serialized data to allocated memory chunk.
+ char *end_address = Write(module, serialized_data);
+ // Verify the allocated memory size is equal to the size of data been written.
+ unsigned int size_written =
+ static_cast<unsigned int>(end_address - serialized_data);
+ if (size_to_alloc != size_written) {
+ BPLOG(ERROR) << "size_to_alloc differs from size_written: "
+ << size_to_alloc << " vs " << size_written;
+ }
+
+ // Set size and return the start address of memory chunk.
+ if (size)
+ *size = size_to_alloc;
+ return serialized_data;
+}
+
+bool ModuleSerializer::SerializeModuleAndLoadIntoFastResolver(
+ const BasicSourceLineResolver::ModuleMap::const_iterator &iter,
+ FastSourceLineResolver *fast_resolver) {
+ BPLOG(INFO) << "Converting symbol " << iter->first.c_str();
+
+ // Cast SourceLineResolverBase::Module* to BasicSourceLineResolver::Module*.
+ BasicSourceLineResolver::Module* basic_module =
+ dynamic_cast<BasicSourceLineResolver::Module*>(iter->second);
+
+ unsigned int size = 0;
+ scoped_array<char> symbol_data(Serialize(*basic_module, &size));
+ if (!symbol_data.get()) {
+ BPLOG(ERROR) << "Serialization failed for module: " << basic_module->name_;
+ return false;
+ }
+ BPLOG(INFO) << "Serialized Symbol Size " << size;
+
+ // Copy the data into string.
+ // Must pass string to LoadModuleUsingMapBuffer(), instead of passing char* to
+ // LoadModuleUsingMemoryBuffer(), becaused of data ownership/lifetime issue.
+ string symbol_data_string(symbol_data.get(), size);
+ symbol_data.reset();
+
+ scoped_ptr<CodeModule> code_module(
+ new BasicCodeModule(0, 0, iter->first, "", "", "", ""));
+
+ return fast_resolver->LoadModuleUsingMapBuffer(code_module.get(),
+ symbol_data_string);
+}
+
+void ModuleSerializer::ConvertAllModules(
+ const BasicSourceLineResolver *basic_resolver,
+ FastSourceLineResolver *fast_resolver) {
+ // Check for NULL pointer.
+ if (!basic_resolver || !fast_resolver)
+ return;
+
+ // Traverse module list in basic resolver.
+ BasicSourceLineResolver::ModuleMap::const_iterator iter;
+ iter = basic_resolver->modules_->begin();
+ for (; iter != basic_resolver->modules_->end(); ++iter)
+ SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver);
+}
+
+bool ModuleSerializer::ConvertOneModule(
+ const string &moduleid,
+ const BasicSourceLineResolver *basic_resolver,
+ FastSourceLineResolver *fast_resolver) {
+ // Check for NULL pointer.
+ if (!basic_resolver || !fast_resolver)
+ return false;
+
+ BasicSourceLineResolver::ModuleMap::const_iterator iter;
+ iter = basic_resolver->modules_->find(moduleid);
+ if (iter == basic_resolver->modules_->end())
+ return false;
+
+ return SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver);
+}
+
+char* ModuleSerializer::SerializeSymbolFileData(
+ const string &symbol_data, unsigned int *size) {
+ scoped_ptr<BasicSourceLineResolver::Module> module(
+ new BasicSourceLineResolver::Module("no name"));
+ scoped_array<char> buffer(new char[symbol_data.size() + 1]);
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.h b/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.h
new file mode 100644
index 000000000..effb00916
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/module_serializer.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// module_serializer.h: ModuleSerializer serializes a loaded symbol,
+// i.e., a loaded BasicSouceLineResolver::Module instance, into a memory
+// chunk of data. The serialized data can be read and loaded by
+// FastSourceLineResolver without CPU & memory-intensive parsing.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_MODULE_SERIALIZER_H__
+#define PROCESSOR_MODULE_SERIALIZER_H__
+
+#include <map>
+#include <string>
+
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/fast_source_line_resolver.h"
+#include "processor/basic_source_line_resolver_types.h"
+#include "processor/fast_source_line_resolver_types.h"
+#include "processor/linked_ptr.h"
+#include "processor/map_serializers-inl.h"
+#include "processor/simple_serializer-inl.h"
+#include "processor/windows_frame_info.h"
+
+namespace google_breakpad {
+
+// ModuleSerializer serializes a loaded BasicSourceLineResolver::Module into a
+// chunk of memory data. ModuleSerializer also provides interface to compute
+// memory size of the serialized data, write serialized data directly into
+// memory, convert ASCII format symbol data into serialized binary data, and
+// convert loaded BasicSourceLineResolver::Module into
+// FastSourceLineResolver::Module.
+class ModuleSerializer {
+ public:
+ // Compute the size of memory required to serialize a module. Return the
+ // total size needed for serialization.
+ size_t SizeOf(const BasicSourceLineResolver::Module &module);
+
+ // Write a module into an allocated memory chunk with required size.
+ // Return the "end" of data, i.e., the address after the final byte of data.
+ char* Write(const BasicSourceLineResolver::Module &module, char *dest);
+
+ // Serializes a loaded Module object into a chunk of memory data and returns
+ // the address of memory chunk. If size != NULL, *size is set to the memory
+ // size allocated for the serialized data.
+ // Caller takes the ownership of the memory chunk (allocated on heap), and
+ // owner should call delete [] to free the memory after use.
+ char* Serialize(const BasicSourceLineResolver::Module &module,
+ unsigned int *size = NULL);
+
+ // Given the string format symbol_data, produces a chunk of serialized data.
+ // Caller takes ownership of the serialized data (on heap), and owner should
+ // call delete [] to free the memory after use.
+ char* SerializeSymbolFileData(const string &symbol_data,
+ unsigned int *size = NULL);
+
+ // Serializes one loaded module with given moduleid in the basic source line
+ // resolver, and loads the serialized data into the fast source line resolver.
+ // Return false if the basic source line doesn't have a module with the given
+ // moduleid.
+ bool ConvertOneModule(const string &moduleid,
+ const BasicSourceLineResolver *basic_resolver,
+ FastSourceLineResolver *fast_resolver);
+
+ // Serializes all the loaded modules in a basic source line resolver, and
+ // loads the serialized data into a fast source line resolver.
+ void ConvertAllModules(const BasicSourceLineResolver *basic_resolver,
+ FastSourceLineResolver *fast_resolver);
+
+ private:
+ // Convenient type names.
+ typedef BasicSourceLineResolver::Line Line;
+ typedef BasicSourceLineResolver::Function Function;
+ typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
+
+ // Internal implementation for ConvertOneModule and ConvertAllModules methods.
+ bool SerializeModuleAndLoadIntoFastResolver(
+ const BasicSourceLineResolver::ModuleMap::const_iterator &iter,
+ FastSourceLineResolver *fast_resolver);
+
+ // Number of Maps that Module class contains.
+ static const 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_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_MODULE_SERIALIZER_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/moz.build b/toolkit/crashreporter/google-breakpad/src/processor/moz.build
new file mode 100644
index 000000000..1a2fac39e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/moz.build
@@ -0,0 +1,66 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ '../third_party/libdisasm/ia32_invariant.c',
+ 'disassembler_x86.cc',
+ 'exploitability_win.cc',
+]
+
+UNIFIED_SOURCES += [
+ '../third_party/libdisasm/ia32_implicit.c',
+ '../third_party/libdisasm/ia32_insn.c',
+ '../third_party/libdisasm/ia32_modrm.c',
+ '../third_party/libdisasm/ia32_opcode_tables.c',
+ '../third_party/libdisasm/ia32_operand.c',
+ '../third_party/libdisasm/ia32_reg.c',
+ '../third_party/libdisasm/ia32_settings.c',
+ '../third_party/libdisasm/x86_disasm.c',
+ '../third_party/libdisasm/x86_imm.c',
+ '../third_party/libdisasm/x86_insn.c',
+ '../third_party/libdisasm/x86_misc.c',
+ '../third_party/libdisasm/x86_operand_list.c',
+ 'basic_code_modules.cc',
+ 'basic_source_line_resolver.cc',
+ 'call_stack.cc',
+ 'cfi_frame_info.cc',
+ 'dump_context.cc',
+ 'dump_object.cc',
+ 'exploitability.cc',
+ 'exploitability_linux.cc',
+ 'logging.cc',
+ 'minidump.cc',
+ 'minidump_processor.cc',
+ 'pathname_stripper.cc',
+ 'proc_maps_linux.cc',
+ 'process_state.cc',
+ 'source_line_resolver_base.cc',
+ 'stack_frame_symbolizer.cc',
+ 'stackwalk_common.cc',
+ 'stackwalker.cc',
+ 'stackwalker_amd64.cc',
+ 'stackwalker_arm.cc',
+ 'stackwalker_arm64.cc',
+ 'stackwalker_mips.cc',
+ 'stackwalker_ppc.cc',
+ 'stackwalker_ppc64.cc',
+ 'stackwalker_sparc.cc',
+ 'stackwalker_x86.cc',
+ 'symbolic_constants_win.cc',
+ 'tokenize.cc',
+]
+
+DEFINES['BPLOG_MINIMUM_SEVERITY'] = 'SEVERITY_ERROR'
+
+Library('breakpad_processor')
+
+# Don't use the STL wrappers in the crashreporter clients
+DISABLE_STL_WRAPPING = True
+
+# We allow warnings for third-party code that can be updated from upstream.
+ALLOW_COMPILER_WARNINGS = True
+
+include('/toolkit/crashreporter/crashreporter.mozbuild')
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.cc b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.cc
new file mode 100644
index 000000000..839287bdb
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// pathname_stripper.cc: Manipulates pathnames into their component parts.
+//
+// See pathname_stripper.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "processor/pathname_stripper.h"
+
+namespace google_breakpad {
+
+// static
+string PathnameStripper::File(const string &path) {
+ string::size_type slash = path.rfind('/');
+ string::size_type backslash = path.rfind('\\');
+
+ string::size_type file_start = 0;
+ if (slash != string::npos &&
+ (backslash == string::npos || slash > backslash)) {
+ file_start = slash + 1;
+ } else if (backslash != string::npos) {
+ file_start = backslash + 1;
+ }
+
+ return path.substr(file_start);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.h b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.h
new file mode 100644
index 000000000..423ca0d05
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// pathname_stripper.h: Manipulates pathnames into their component parts.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_PATHNAME_STRIPPER_H__
+#define PROCESSOR_PATHNAME_STRIPPER_H__
+
+#include <string>
+
+#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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc
new file mode 100644
index 000000000..1bff4cb01
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+
+#include "processor/pathname_stripper.h"
+#include "processor/logging.h"
+
+#define ASSERT_TRUE(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
+ return false; \
+ }
+
+#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
+
+namespace {
+
+using google_breakpad::PathnameStripper;
+
+static bool RunTests() {
+ ASSERT_EQ(PathnameStripper::File("/dir/file"), "file");
+ ASSERT_EQ(PathnameStripper::File("\\dir\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("/dir\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("\\dir/file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir/file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir/\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir\\/file"), "file");
+ ASSERT_EQ(PathnameStripper::File("file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir/"), "");
+ ASSERT_EQ(PathnameStripper::File("dir\\"), "");
+ ASSERT_EQ(PathnameStripper::File("dir/dir/"), "");
+ ASSERT_EQ(PathnameStripper::File("dir\\dir\\"), "");
+ ASSERT_EQ(PathnameStripper::File("dir1/dir2/file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir1\\dir2\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir1/dir2\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir1\\dir2/file"), "file");
+ ASSERT_EQ(PathnameStripper::File(""), "");
+ ASSERT_EQ(PathnameStripper::File("1"), "1");
+ ASSERT_EQ(PathnameStripper::File("1/2"), "2");
+ ASSERT_EQ(PathnameStripper::File("1\\2"), "2");
+ ASSERT_EQ(PathnameStripper::File("/1/2"), "2");
+ ASSERT_EQ(PathnameStripper::File("\\1\\2"), "2");
+ ASSERT_EQ(PathnameStripper::File("dir//file"), "file");
+ ASSERT_EQ(PathnameStripper::File("dir\\\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("/dir//file"), "file");
+ ASSERT_EQ(PathnameStripper::File("\\dir\\\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("c:\\dir\\file"), "file");
+ ASSERT_EQ(PathnameStripper::File("c:\\dir\\file.ext"), "file.ext");
+
+ return true;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ return RunTests() ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h
new file mode 100644
index 000000000..d7dbeac20
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h
@@ -0,0 +1,363 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression
+// evaluator.
+//
+// Documentation in postfix_evaluator.h.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__
+#define PROCESSOR_POSTFIX_EVALUATOR_INL_H__
+
+#include "processor/postfix_evaluator.h"
+
+#include <stdio.h>
+
+#include <sstream>
+
+#include "google_breakpad/processor/memory_region.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+using std::istringstream;
+using std::ostringstream;
+
+
+// A small class used in Evaluate to make sure to clean up the stack
+// before returning failure.
+class AutoStackClearer {
+ public:
+ explicit AutoStackClearer(vector<string> *stack) : stack_(stack) {}
+ ~AutoStackClearer() { stack_->clear(); }
+
+ private:
+ vector<string> *stack_;
+};
+
+
+template<typename ValueType>
+bool PostfixEvaluator<ValueType>::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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h
new file mode 100644
index 000000000..94b66190d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h
@@ -0,0 +1,179 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// postfix_evaluator.h: Postfix (reverse Polish) notation expression evaluator.
+//
+// PostfixEvaluator evaluates an expression, using the expression itself
+// in postfix (reverse Polish) notation and a dictionary mapping constants
+// and variables to their values. The evaluator supports standard
+// arithmetic operations, assignment into variables, and when an optional
+// MemoryRange is provided, dereferencing. (Any unary key-to-value operation
+// may be used with a MemoryRange implementation that returns the appropriate
+// values, but PostfixEvaluator was written with dereferencing in mind.)
+//
+// The expression language is simple. Expressions are supplied as strings,
+// with operands and operators delimited by whitespace. Operands may be
+// either literal values suitable for ValueType, or constants or variables,
+// which reference the dictionary. The supported binary operators are +
+// (addition), - (subtraction), * (multiplication), / (quotient of division),
+// % (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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc
new file mode 100644
index 000000000..f11898284
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator.
+//
+// Author: Mark Mentovai
+
+#include <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;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux.cc b/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux.cc
new file mode 100644
index 000000000..3c0dea25d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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
+
+#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", &region.start, &region.end, permissions,
+ &region.offset, &region.major_device, &region.minor_device,
+ &region.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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux_unittest.cc
new file mode 100644
index 000000000..466f23455
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/proc_maps_linux_unittest.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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("", &regions));
+ 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, &regions));
+ 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, &regions));
+ 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, &regions));
+}
+
+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, &regions));
+ 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, &regions));
+ 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, &regions));
+ 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, &regions));
+ 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, &regions));
+ 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, &regions));
+ 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], &regions));
+ }
+}
+
+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], &regions));
+ }
+}
+
+TEST(ProcMapsTest, ParseProcMapsEmptyString) {
+ std::vector<google_breakpad::MappedMemoryRegion> regions;
+ EXPECT_TRUE(ParseProcMaps("", &regions));
+ 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, &regions));
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc
new file mode 100644
index 000000000..5a5cd7f62
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// process_state.cc: A snapshot of a process, in a fully-digested state.
+//
+// See process_state.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "google_breakpad/processor/process_state.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_modules.h"
+
+namespace google_breakpad {
+
+ProcessState::~ProcessState() {
+ Clear();
+}
+
+void ProcessState::Clear() {
+ time_date_stamp_ = 0;
+ 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();
+ // 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;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/processor.gyp b/toolkit/crashreporter/google-breakpad/src/processor/processor.gyp
new file mode 100644
index 000000000..083a3237f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/processor.gyp
@@ -0,0 +1,184 @@
+# Copyright 2014 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+ 'includes': [
+ '../build/common.gypi',
+ 'processor_tools.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'processor',
+ 'type': 'static_library',
+ 'sources': [
+ 'address_map-inl.h',
+ 'address_map.h',
+ 'basic_code_module.h',
+ 'basic_code_modules.cc',
+ 'basic_code_modules.h',
+ 'basic_source_line_resolver.cc',
+ 'basic_source_line_resolver_types.h',
+ 'call_stack.cc',
+ 'cfi_frame_info-inl.h',
+ 'cfi_frame_info.cc',
+ 'cfi_frame_info.h',
+ 'contained_range_map-inl.h',
+ 'contained_range_map.h',
+ 'disassembler_x86.cc',
+ 'disassembler_x86.h',
+ 'dump_context.cc',
+ 'dump_object.cc',
+ 'exploitability.cc',
+ 'exploitability_linux.cc',
+ 'exploitability_linux.h',
+ 'exploitability_win.cc',
+ 'exploitability_win.h',
+ 'fast_source_line_resolver.cc',
+ 'fast_source_line_resolver_types.h',
+ 'linked_ptr.h',
+ 'logging.cc',
+ 'logging.h',
+ 'map_serializers-inl.h',
+ 'map_serializers.h',
+ 'microdump_processor.cc',
+ 'minidump.cc',
+ 'minidump_processor.cc',
+ 'module_comparer.cc',
+ 'module_comparer.h',
+ 'module_factory.h',
+ 'module_serializer.cc',
+ 'module_serializer.h',
+ 'pathname_stripper.cc',
+ 'pathname_stripper.h',
+ 'postfix_evaluator-inl.h',
+ 'postfix_evaluator.h',
+ 'proc_maps_linux.cc',
+ 'process_state.cc',
+ 'range_map-inl.h',
+ 'range_map.h',
+ 'simple_serializer-inl.h',
+ 'simple_serializer.h',
+ 'simple_symbol_supplier.cc',
+ 'simple_symbol_supplier.h',
+ 'source_line_resolver_base.cc',
+ 'source_line_resolver_base_types.h',
+ 'stack_frame_cpu.cc',
+ 'stack_frame_symbolizer.cc',
+ 'stackwalk_common.cc',
+ 'stackwalk_common.h',
+ 'stackwalker.cc',
+ 'stackwalker_address_list.cc',
+ 'stackwalker_address_list.h',
+ 'stackwalker_amd64.cc',
+ 'stackwalker_amd64.h',
+ 'stackwalker_arm.cc',
+ 'stackwalker_arm.h',
+ 'stackwalker_arm64.cc',
+ 'stackwalker_arm64.h',
+ 'stackwalker_mips.cc',
+ 'stackwalker_mips.h',
+ 'stackwalker_ppc.cc',
+ 'stackwalker_ppc.h',
+ 'stackwalker_ppc64.cc',
+ 'stackwalker_ppc64.h',
+ 'stackwalker_selftest.cc',
+ 'stackwalker_sparc.cc',
+ 'stackwalker_sparc.h',
+ 'stackwalker_x86.cc',
+ 'stackwalker_x86.h',
+ 'static_address_map-inl.h',
+ 'static_address_map.h',
+ 'static_contained_range_map-inl.h',
+ 'static_contained_range_map.h',
+ 'static_map-inl.h',
+ 'static_map.h',
+ 'static_map_iterator-inl.h',
+ 'static_map_iterator.h',
+ 'static_range_map-inl.h',
+ 'static_range_map.h',
+ 'symbolic_constants_win.cc',
+ 'symbolic_constants_win.h',
+ 'synth_minidump.cc',
+ 'synth_minidump.h',
+ 'tokenize.cc',
+ 'tokenize.h',
+ 'windows_frame_info.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../common/common.gyp:common',
+ '../third_party/libdisasm/libdisasm.gyp:libdisasm',
+ ],
+ },
+ {
+ 'target_name': 'processor_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'address_map_unittest.cc',
+ 'basic_source_line_resolver_unittest.cc',
+ 'cfi_frame_info_unittest.cc',
+ 'contained_range_map_unittest.cc',
+ 'disassembler_x86_unittest.cc',
+ 'exploitability_unittest.cc',
+ 'fast_source_line_resolver_unittest.cc',
+ 'map_serializers_unittest.cc',
+ 'microdump_processor_unittest.cc',
+ 'minidump_processor_unittest.cc',
+ 'minidump_unittest.cc',
+ 'pathname_stripper_unittest.cc',
+ 'postfix_evaluator_unittest.cc',
+ 'range_map_shrink_down_unittest.cc',
+ 'range_map_unittest.cc',
+ 'stackwalker_address_list_unittest.cc',
+ 'stackwalker_amd64_unittest.cc',
+ 'stackwalker_arm64_unittest.cc',
+ 'stackwalker_arm_unittest.cc',
+ 'stackwalker_mips_unittest.cc',
+ 'stackwalker_mips64_unittest.cc',
+ 'stackwalker_unittest_utils.h',
+ 'stackwalker_x86_unittest.cc',
+ 'static_address_map_unittest.cc',
+ 'static_contained_range_map_unittest.cc',
+ 'static_map_unittest.cc',
+ 'static_range_map_unittest.cc',
+ 'synth_minidump_unittest.cc',
+ 'synth_minidump_unittest_data.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ 'processor',
+ '../build/testing.gypi:gmock',
+ '../build/testing.gypi:gtest',
+ ],
+ },
+ ],
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/processor_tools.gypi b/toolkit/crashreporter/google-breakpad/src/processor/processor_tools.gypi
new file mode 100644
index 000000000..ecb450d60
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/processor_tools.gypi
@@ -0,0 +1,57 @@
+# Copyright 2014 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+ 'target_defaults': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'minidump_dump',
+ 'type': 'executable',
+ 'sources': [
+ 'minidump_dump.cc',
+ ],
+ 'dependencies': [
+ 'processor',
+ ],
+ },
+ {
+ 'target_name': 'minidump_stackwalk',
+ 'type': 'executable',
+ 'sources': [
+ 'minidump_stackwalk.cc',
+ ],
+ 'dependencies': [
+ 'processor',
+ ],
+ },
+ ],
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/proto/README b/toolkit/crashreporter/google-breakpad/src/processor/proto/README
new file mode 100644
index 000000000..df37b6f39
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/proto/README
@@ -0,0 +1,20 @@
+If you wish to use these protobufs, you must generate their source files
+using protoc from the protobuf project (http://code.google.com/p/protobuf/).
+
+-----
+Troubleshooting for Protobuf:
+
+Install:
+If you are getting permission errors install, make sure you are not trying to
+install from an NFS.
+
+
+Running protoc:
+protoc: error while loading shared libraries: libprotobuf.so.0: cannot open
+shared object file: No such file or directory
+
+The issue is that Ubuntu 8.04 doesn't include /usr/local/lib in
+library paths.
+
+To fix it for your current terminal session, just type in
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/proto/process_state.proto b/toolkit/crashreporter/google-breakpad/src/processor/proto/process_state.proto
new file mode 100644
index 000000000..d3e02dc3f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/proto/process_state.proto
@@ -0,0 +1,210 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// 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;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h
new file mode 100644
index 000000000..9fe74c502
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h
@@ -0,0 +1,272 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// range_map-inl.h: Range map implementation.
+//
+// See range_map.h for documentation.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_RANGE_MAP_INL_H__
+#define PROCESSOR_RANGE_MAP_INL_H__
+
+
+#include <assert.h>
+
+#include "processor/range_map.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+void RangeMap<AddressType, EntryType>::SetEnableShrinkDown(
+ bool enable_shrink_down) {
+ enable_shrink_down_ = enable_shrink_down;
+}
+
+template<typename AddressType, typename EntryType>
+bool RangeMap<AddressType, EntryType>::IsShrinkDownEnabled() const {
+ return enable_shrink_down_;
+}
+
+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 = base + (size - 1);
+
+ // Check for undersize or overflow.
+ if (size <= 0 || high < base) {
+ // The processor will hit this case too frequently with common symbol
+ // files in the size == 0 case, which is more suited to a DEBUG channel.
+ // Filter those out since there's no DEBUG channel at the moment.
+ BPLOG_IF(INFO, size != 0) << "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 begins in the space used by this range. It may be
+ // contained within the space used by this range, or it may extend lower.
+ // If enable_shrink_down_ is true, shrink the current range down, otherwise
+ // this is an error.
+ if (enable_shrink_down_) {
+ 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()) {
+ if (iterator_high->second.base() <= high) {
+ // The range above this one overlaps with this one. It may fully
+ // contain this range, or it may begin within this range and extend
+ // higher. If enable_shrink_down_ is true, shrink the other range down,
+ // otherwise this is an error.
+ if (enable_shrink_down_ && 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 map_.size();
+}
+
+
+template<typename AddressType, typename EntryType>
+void RangeMap<AddressType, EntryType>::Clear() {
+ map_.clear();
+}
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_RANGE_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/range_map.h b/toolkit/crashreporter/google-breakpad/src/processor/range_map.h
new file mode 100644
index 000000000..d90a67327
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/range_map.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// range_map.h: Range maps.
+//
+// A range map associates a range of addresses with a specific object. This
+// is useful when certain objects of variable size are located within an
+// address space. The range map makes it simple to determine which object is
+// associated with a specific address, which may be any address within the
+// range associated with an object.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_RANGE_MAP_H__
+#define PROCESSOR_RANGE_MAP_H__
+
+
+#include <map>
+
+
+namespace google_breakpad {
+
+// Forward declarations (for later friend declarations of specialized template).
+template<class, class> class RangeMapSerializer;
+
+template<typename AddressType, typename EntryType>
+class RangeMap {
+ public:
+ RangeMap() : enable_shrink_down_(false), map_() {}
+
+ // |enable_shrink_down| tells whether overlapping ranges can be shrunk down.
+ // If true, then adding a new range that overlaps with an existing one can
+ // be a successful operation. The range which ends at the higher address
+ // will be shrunk down by moving its start position to a higher address so
+ // that it does not overlap anymore.
+ void SetEnableShrinkDown(bool enable_shrink_down);
+ bool IsShrinkDownEnabled() const;
+
+ // 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;
+
+ // Whether overlapping ranges can be shrunk down.
+ bool enable_shrink_down_;
+
+ // Maps the high address of each range to a EntryType.
+ AddressToRangeMap map_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_RANGE_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/range_map_shrink_down_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/range_map_shrink_down_unittest.cc
new file mode 100644
index 000000000..8dd0e709b
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/range_map_shrink_down_unittest.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+
+// range_map_shrink_down_unittest.cc: Unit tests for RangeMap that specifically
+// test shrink down when ranges overlap.
+//
+// Author: Ivan Penkov
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "processor/range_map-inl.h"
+
+#include "breakpad_googletest_includes.h"
+#include "common/scoped_ptr.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+
+namespace {
+
+using google_breakpad::linked_ptr;
+using google_breakpad::scoped_ptr;
+using google_breakpad::RangeMap;
+
+// A CountedObject holds an int. A global (not thread safe!) count of
+// allocated CountedObjects is maintained to help test memory management.
+class CountedObject {
+ public:
+ explicit CountedObject(int id) : id_(id) { ++count_; }
+ ~CountedObject() { --count_; }
+
+ static int count() { return count_; }
+ int id() const { return id_; }
+
+ private:
+ static int count_;
+ int id_;
+};
+
+int CountedObject::count_;
+
+typedef int AddressType;
+typedef RangeMap<AddressType, linked_ptr<CountedObject>> TestMap;
+
+// Same range cannot be stored wice.
+TEST(RangeMap, TestShinkDown_SameRange) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ 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(RangeMap, TestShinkDown_CompletelyContained) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ // 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(RangeMap, TestShinkDown_CompletelyContained_LargerAddedSecond) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ // 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(RangeMap, TestShinkDown_PartialOverlap_AtBeginning) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ 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(RangeMap, TestShinkDown_PartialOverlap_AtEnd) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ 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(RangeMap, TestShinkDown_OverlapAtBothEnds) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ // 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(RangeMap, TestShinkDown_MultipleConflicts) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ // 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(RangeMap, TestShinkDown_NoConflicts) {
+ TestMap range_map;
+ range_map.SetEnableShrinkDown(true);
+ // 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc
new file mode 100644
index 000000000..31b89e5de
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc
@@ -0,0 +1,559 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// range_map_unittest.cc: Unit tests for RangeMap
+//
+// Author: Mark Mentovai
+
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "processor/range_map-inl.h"
+
+#include "common/scoped_ptr.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+
+namespace {
+
+
+using google_breakpad::linked_ptr;
+using google_breakpad::scoped_ptr;
+using google_breakpad::RangeMap;
+
+
+// A CountedObject holds an int. A global (not thread safe!) count of
+// allocated CountedObjects is maintained to help test memory management.
+class CountedObject {
+ public:
+ explicit CountedObject(int id) : id_(id) { ++count_; }
+ ~CountedObject() { --count_; }
+
+ static int count() { return count_; }
+ int id() const { return id_; }
+
+ private:
+ static int count_;
+ int id_;
+};
+
+int CountedObject::count_;
+
+
+typedef int AddressType;
+typedef RangeMap< AddressType, linked_ptr<CountedObject> > TestMap;
+
+
+// RangeTest contains data to use for store and retrieve tests. See
+// RunTests for descriptions of the tests.
+struct RangeTest {
+ // Base address to use for test
+ AddressType address;
+
+ // Size of range to use for test
+ AddressType size;
+
+ // Unique ID of range - unstorable ranges must have unique IDs too
+ int id;
+
+ // Whether this range is expected to be stored successfully or not
+ bool expect_storable;
+};
+
+
+// A RangeTestSet encompasses multiple RangeTests, which are run in
+// sequence on the same RangeMap.
+struct RangeTestSet {
+ // An array of RangeTests
+ const RangeTest *range_tests;
+
+ // The number of tests in the set
+ unsigned int range_test_count;
+};
+
+
+// StoreTest uses the data in a RangeTest and calls StoreRange on the
+// test RangeMap. It returns true if the expected result occurred, and
+// false if something else happened.
+static bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
+ linked_ptr<CountedObject> object(new CountedObject(range_test->id));
+ bool stored = range_map->StoreRange(range_test->address,
+ range_test->size,
+ object);
+
+ if (stored != range_test->expect_storable) {
+ fprintf(stderr, "FAILED: "
+ "StoreRange id %d, expected %s, observed %s\n",
+ range_test->id,
+ range_test->expect_storable ? "storable" : "not storable",
+ stored ? "stored" : "not stored");
+ return false;
+ }
+
+ return true;
+}
+
+
+// RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
+// test RangeMap. If it retrieves the expected value (which can be no
+// map entry at the specified range,) it returns true, otherwise, it returns
+// false. RetrieveTest will check the values around the base address and
+// the high address of a range to guard against off-by-one errors.
+static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
+ for (unsigned int side = 0; side <= 1; ++side) {
+ // When side == 0, check the low side (base address) of each range.
+ // When side == 1, check the high side (base + size) of each range.
+
+ // Check one-less and one-greater than the target address in addition
+ // to the target address itself.
+
+ // If the size of the range is only 1, don't check one greater than
+ // the base or one less than the high - for a successfully stored
+ // range, these tests would erroneously fail because the range is too
+ // small.
+ AddressType low_offset = -1;
+ AddressType high_offset = 1;
+ if (range_test->size == 1) {
+ if (!side) // When checking the low side,
+ high_offset = 0; // don't check one over the target.
+ else // When checking the high side,
+ low_offset = 0; // don't check one under the target.
+ }
+
+ for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
+ AddressType address =
+ offset +
+ (!side ? range_test->address :
+ range_test->address + range_test->size - 1);
+
+ bool expected_result = false; // This is correct for tests not stored.
+ if (range_test->expect_storable) {
+ if (offset == 0) // When checking the target address,
+ expected_result = true; // test should always succeed.
+ else if (offset == -1) // When checking one below the target,
+ expected_result = side; // should fail low and succeed high.
+ else // When checking one above the target,
+ expected_result = !side; // should succeed low and fail high.
+ }
+
+ linked_ptr<CountedObject> object;
+ AddressType retrieved_base = AddressType();
+ AddressType retrieved_size = AddressType();
+ 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;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer-inl.h
new file mode 100644
index 000000000..606bb3cea
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer-inl.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// simple_serializer-inl.h: template specializations for following types:
+// bool, const char *(C-string), string,
+// Line, Function, PublicSymbol, WindowsFrameInfo and their linked pointers.
+//
+// See simple_serializer.h for moredocumentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_SIMPLE_SERIALIZER_INL_H__
+#define PROCESSOR_SIMPLE_SERIALIZER_INL_H__
+
+#include <string>
+
+#include "processor/simple_serializer.h"
+#include "map_serializers-inl.h"
+
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "processor/basic_source_line_resolver_types.h"
+#include "processor/linked_ptr.h"
+#include "processor/windows_frame_info.h"
+
+namespace google_breakpad {
+
+// Specializations of SimpleSerializer: bool
+template<>
+class SimpleSerializer<bool> {
+ public:
+ static size_t SizeOf(bool boolean) { return 1; }
+
+ static char *Write(bool boolean, char *dest) {
+ *dest = static_cast<char>(boolean? 255 : 0);
+ return ++dest;
+ }
+
+ 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: PublicSymbol
+template<>
+class SimpleSerializer<BasicSourceLineResolver::PublicSymbol> {
+ typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
+ public:
+ static size_t SizeOf(const PublicSymbol &pubsymbol) {
+ return SimpleSerializer<string>::SizeOf(pubsymbol.name)
+ + SimpleSerializer<MemAddr>::SizeOf(pubsymbol.address)
+ + SimpleSerializer<int32_t>::SizeOf(pubsymbol.parameter_size);
+ }
+ static char *Write(const PublicSymbol &pubsymbol, char *dest) {
+ dest = SimpleSerializer<string>::Write(pubsymbol.name, dest);
+ dest = SimpleSerializer<MemAddr>::Write(pubsymbol.address, dest);
+ dest = SimpleSerializer<int32_t>::Write(pubsymbol.parameter_size, dest);
+ return dest;
+ }
+};
+
+// Specializations of SimpleSerializer: WindowsFrameInfo
+template<>
+class SimpleSerializer<WindowsFrameInfo> {
+ public:
+ static size_t SizeOf(const WindowsFrameInfo &wfi) {
+ unsigned int size = 0;
+ size += 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, Function, PublicSymbol, WindowsFrameInfo.
+template<>
+class SimpleSerializer< linked_ptr<BasicSourceLineResolver::Line> > {
+ typedef BasicSourceLineResolver::Line Line;
+ public:
+ static size_t SizeOf(const linked_ptr<Line> &lineptr) {
+ if (lineptr.get() == NULL) return 0;
+ return SimpleSerializer<Line>::SizeOf(*(lineptr.get()));
+ }
+ static char *Write(const linked_ptr<Line> &lineptr, char *dest) {
+ if (lineptr.get())
+ dest = SimpleSerializer<Line>::Write(*(lineptr.get()), dest);
+ return dest;
+ }
+};
+
+template<>
+class SimpleSerializer<BasicSourceLineResolver::Function> {
+ // Convenient type names.
+ typedef BasicSourceLineResolver::Function Function;
+ typedef BasicSourceLineResolver::Line Line;
+ public:
+ static size_t SizeOf(const Function &func) {
+ unsigned int size = 0;
+ size += SimpleSerializer<string>::SizeOf(func.name);
+ size += SimpleSerializer<MemAddr>::SizeOf(func.address);
+ size += SimpleSerializer<MemAddr>::SizeOf(func.size);
+ size += SimpleSerializer<int32_t>::SizeOf(func.parameter_size);
+ size += range_map_serializer_.SizeOf(func.lines);
+ return size;
+ }
+
+ static char *Write(const Function &func, char *dest) {
+ dest = SimpleSerializer<string>::Write(func.name, dest);
+ dest = SimpleSerializer<MemAddr>::Write(func.address, dest);
+ dest = SimpleSerializer<MemAddr>::Write(func.size, dest);
+ dest = SimpleSerializer<int32_t>::Write(func.parameter_size, dest);
+ dest = range_map_serializer_.Write(func.lines, dest);
+ return dest;
+ }
+ private:
+ // This static member is defined in module_serializer.cc.
+ static RangeMapSerializer< MemAddr, linked_ptr<Line> > range_map_serializer_;
+};
+
+template<>
+class SimpleSerializer< linked_ptr<BasicSourceLineResolver::Function> > {
+ typedef BasicSourceLineResolver::Function Function;
+ public:
+ static size_t SizeOf(const linked_ptr<Function> &func) {
+ if (!func.get()) return 0;
+ return SimpleSerializer<Function>::SizeOf(*(func.get()));
+ }
+
+ static char *Write(const linked_ptr<Function> &func, char *dest) {
+ if (func.get())
+ dest = SimpleSerializer<Function>::Write(*(func.get()), dest);
+ return dest;
+ }
+};
+
+template<>
+class SimpleSerializer< linked_ptr<BasicSourceLineResolver::PublicSymbol> > {
+ typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
+ public:
+ static size_t SizeOf(const linked_ptr<PublicSymbol> &pubsymbol) {
+ if (pubsymbol.get() == NULL) return 0;
+ return SimpleSerializer<PublicSymbol>::SizeOf(*(pubsymbol.get()));
+ }
+ static char *Write(const linked_ptr<PublicSymbol> &pubsymbol, char *dest) {
+ if (pubsymbol.get())
+ dest = SimpleSerializer<PublicSymbol>::Write(*(pubsymbol.get()), dest);
+ return dest;
+ }
+};
+
+template<>
+class SimpleSerializer< linked_ptr<WindowsFrameInfo> > {
+ public:
+ static size_t SizeOf(const linked_ptr<WindowsFrameInfo> &wfi) {
+ if (wfi.get() == NULL) return 0;
+ return SimpleSerializer<WindowsFrameInfo>::SizeOf(*(wfi.get()));
+ }
+ static char *Write(const linked_ptr<WindowsFrameInfo> &wfi, char *dest) {
+ if (wfi.get())
+ dest = SimpleSerializer<WindowsFrameInfo>::Write(*(wfi.get()), dest);
+ return dest;
+ }
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_SIMPLE_SERIALIZER_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer.h b/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer.h
new file mode 100644
index 000000000..275f51ce3
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/simple_serializer.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// simple_serializer.h: SimpleSerializer is a template for calculating size and
+// writing to specific memory location for objects of primitive types, C-style
+// string, string, breakpad types/structs etc.
+// All specializations of SimpleSerializer template are defined in the
+// "simple_serializer-inl.h" file.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_SIMPLE_SERIALIZER_H__
+#define PROCESSOR_SIMPLE_SERIALIZER_H__
+
+#include "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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc b/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc
new file mode 100644
index 000000000..bc5ebb687
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// simple_symbol_supplier.cc: A simple SymbolSupplier implementation
+//
+// See simple_symbol_supplier.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "processor/simple_symbol_supplier.h"
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+
+#include "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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h b/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h
new file mode 100644
index 000000000..0cde85cdc
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// simple_symbol_supplier.h: A simple SymbolSupplier implementation
+//
+// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
+// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
+// created with one or more base directories, which are the root paths for all
+// symbol files. Each symbol file contained therein has a directory entry in
+// the base directory with a name identical to the corresponding debugging
+// file (pdb). Within each of these directories, there are subdirectories
+// named for the debugging file's identifier. For recent pdb files, this is
+// a concatenation of the pdb's uuid and age, presented in hexadecimal form,
+// without any dashes or separators. The uuid is in uppercase hexadecimal
+// and the age is in lowercase hexadecimal. Within that subdirectory,
+// SimpleSymbolSupplier expects to find the symbol file, which is named
+// identically to the debug file, but with a .sym extension. If the original
+// debug file had a name ending in .pdb, the .pdb extension will be replaced
+// with .sym. This sample hierarchy is rooted at the "symbols" base
+// directory:
+//
+// symbols
+// symbols/test_app.pdb
+// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1
+// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym
+// symbols/kernel32.pdb
+// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542
+// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
+//
+// In this case, the uuid of test_app.pdb is
+// 63fe4780-728d-4937-9b9d-7bb6460cb42a and its age is 1.
+//
+// This scheme was chosen to be roughly analogous to the way that
+// symbol files may be accessed from Microsoft Symbol Server. A hierarchy
+// used for Microsoft Symbol Server storage is usable as a hierarchy for
+// SimpleSymbolServer, provided that the pdb files are transformed to dumped
+// format using a tool such as dump_syms, and given a .sym extension.
+//
+// SimpleSymbolSupplier will iterate over all root paths searching for
+// a symbol file existing in that path.
+//
+// SimpleSymbolSupplier supports any debugging file which can be identified
+// by a CodeModule object's debug_file and debug_identifier accessors. The
+// expected ultimate source of these CodeModule objects are MinidumpModule
+// objects; it is this class that is responsible for assigning appropriate
+// values for debug_file and debug_identifier.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
+#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base.cc b/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base.cc
new file mode 100644
index 000000000..6eff1f991
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// source_line_resolver_base.cc: Implementation of SourceLineResolverBase.
+//
+// See source_line_resolver_base.h and source_line_resolver_base_types.h for
+// more documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <map>
+#include <utility>
+
+#include "google_breakpad/processor/source_line_resolver_base.h"
+#include "processor/source_line_resolver_base_types.h"
+#include "processor/module_factory.h"
+
+using std::map;
+using std::make_pair;
+
+namespace google_breakpad {
+
+SourceLineResolverBase::SourceLineResolverBase(
+ ModuleFactory *module_factory)
+ : modules_(new ModuleMap),
+ 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";
+
+ 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) {
+ 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";
+
+ 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) {
+ if (frame->module) {
+ ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
+ if (it != modules_->end()) {
+ it->second->LookupAddress(frame);
+ }
+ }
+}
+
+WindowsFrameInfo *SourceLineResolverBase::FindWindowsFrameInfo(
+ const StackFrame *frame) {
+ if (frame->module) {
+ ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
+ if (it != modules_->end()) {
+ return it->second->FindWindowsFrameInfo(frame);
+ }
+ }
+ return NULL;
+}
+
+CFIFrameInfo *SourceLineResolverBase::FindCFIFrameInfo(
+ const StackFrame *frame) {
+ if (frame->module) {
+ ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
+ if (it != modules_->end()) {
+ return it->second->FindCFIFrameInfo(frame);
+ }
+ }
+ return NULL;
+}
+
+bool SourceLineResolverBase::CompareString::operator()(
+ const string &s1, const string &s2) const {
+ return strcmp(s1.c_str(), s2.c_str()) < 0;
+}
+
+bool SourceLineResolverBase::Module::ParseCFIRuleSet(
+ const string &rule_set, CFIFrameInfo *frame_info) const {
+ CFIFrameInfoParseHandler handler(frame_info);
+ CFIRuleParser parser(&handler);
+ return parser.Parse(rule_set);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base_types.h b/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base_types.h
new file mode 100644
index 000000000..4a9dfb3ce
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/source_line_resolver_base_types.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// source_line_resolver_base_types.h: definition of nested classes/structs in
+// SourceLineResolverBase. It moves the definitions out of
+// source_line_resolver_base.cc, so that other classes may have access
+// to these private nested types without including source_line_resolver_base.cc
+// In addition, Module is defined as a pure abstract class to be implemented by
+// each concrete source line resolver class.
+//
+// See source_line_resolver_base.h for more documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <stdio.h>
+
+#include <map>
+#include <string>
+
+#include "google_breakpad/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/windows_frame_info.h"
+
+#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
+#define PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
+
+namespace google_breakpad {
+
+class SourceLineResolverBase::AutoFileCloser {
+ public:
+ explicit AutoFileCloser(FILE *file) : file_(file) {}
+ ~AutoFileCloser() {
+ if (file_)
+ fclose(file_);
+ }
+
+ private:
+ FILE *file_;
+};
+
+struct SourceLineResolverBase::Line {
+ Line() { }
+ Line(MemAddr addr, MemAddr code_size, int file_id, int source_line)
+ : address(addr)
+ , size(code_size)
+ , source_file_id(file_id)
+ , line(source_line) { }
+
+ MemAddr address;
+ MemAddr size;
+ int32_t source_file_id;
+ int32_t line;
+};
+
+struct SourceLineResolverBase::Function {
+ Function() { }
+ Function(const string &function_name,
+ MemAddr function_address,
+ MemAddr code_size,
+ int set_parameter_size)
+ : name(function_name), address(function_address), size(code_size),
+ parameter_size(set_parameter_size) { }
+
+ string name;
+ MemAddr address;
+ MemAddr size;
+
+ // The size of parameters passed to this function on the stack.
+ int32_t parameter_size;
+};
+
+struct SourceLineResolverBase::PublicSymbol {
+ PublicSymbol() { }
+ PublicSymbol(const string& set_name,
+ MemAddr set_address,
+ int set_parameter_size)
+ : name(set_name),
+ address(set_address),
+ parameter_size(set_parameter_size) {}
+
+ string name;
+ MemAddr address;
+
+ // If the public symbol is used as a function entry point, parameter_size
+ // is set to the size of the parameters passed to the funciton on the
+ // stack, if known.
+ int32_t parameter_size;
+};
+
+class SourceLineResolverBase::Module {
+ public:
+ virtual ~Module() { };
+ // Loads a map from the given buffer in char* type.
+ // Does NOT take ownership of memory_buffer (the caller, source line resolver,
+ // is the owner of memory_buffer).
+ // 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) 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_cpu.cc b/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_cpu.cc
new file mode 100644
index 000000000..6175dc7f2
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_cpu.cc
@@ -0,0 +1,79 @@
+// Copyright 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stack_frame_cpu.h: CPU-specific StackFrame extensions.
+//
+// See google_breakpad/processor/stack_frame_cpu.h for documentation.
+//
+// Author: Colin Blundell
+
+#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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_symbolizer.cc b/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_symbolizer.cc
new file mode 100644
index 000000000..5c8dbe5e1
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_symbolizer.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// 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.
+
+#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 SystemInfo* system_info,
+ StackFrame* frame) {
+ assert(frame);
+
+ if (!modules) return kError;
+ const CodeModule* module = 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);
+ 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);
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.cc
new file mode 100644
index 000000000..704039f34
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.cc
@@ -0,0 +1,950 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalk_common.cc: Module shared by the {micro,mini}dump_stackwalck
+// executables to print the content of dumps (w/ stack traces) on the console.
+//
+// Author: Mark Mentovai
+
+#include "processor/stackwalk_common.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "common/stdio_wrapper.h"
+#include "common/using_std_string.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/process_state.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+#include "processor/pathname_stripper.h"
+
+namespace google_breakpad {
+
+namespace {
+
+using std::vector;
+
+// Separator character for machine readable output.
+static const char kOutputSeparator = '|';
+
+// PrintRegister prints a register's name and value to stdout. It will
+// print four registers on a line. For the first register in a set,
+// pass 0 for |start_col|. For registers in a set, pass the most recent
+// return value of PrintRegister.
+// The caller is responsible for printing the final newline after a set
+// of registers is completely printed, regardless of the number of calls
+// to PrintRegister.
+static const int kMaxWidth = 80; // optimize for an 80-column terminal
+static int PrintRegister(const char *name, uint32_t value, int start_col) {
+ char buffer[64];
+ snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value);
+
+ if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
+ start_col = 0;
+ printf("\n ");
+ }
+ fputs(buffer, stdout);
+
+ return start_col + strlen(buffer);
+}
+
+// PrintRegister64 does the same thing, but for 64-bit registers.
+static int PrintRegister64(const char *name, uint64_t value, int start_col) {
+ char buffer[64];
+ snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value);
+
+ if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
+ start_col = 0;
+ printf("\n ");
+ }
+ fputs(buffer, stdout);
+
+ return start_col + strlen(buffer);
+}
+
+// StripSeparator takes a string |original| and returns a copy
+// of the string with all occurences of |kOutputSeparator| removed.
+static string StripSeparator(const string &original) {
+ string result = original;
+ string::size_type position = 0;
+ while ((position = result.find(kOutputSeparator, position)) != string::npos) {
+ result.erase(position, 1);
+ }
+ position = 0;
+ while ((position = result.find('\n', position)) != string::npos) {
+ result.erase(position, 1);
+ }
+ return result;
+}
+
+// PrintStackContents prints the stack contents of the current frame to stdout.
+static void PrintStackContents(const string &indent,
+ const StackFrame *frame,
+ const StackFrame *prev_frame,
+ const string &cpu,
+ const MemoryRegion *memory,
+ const CodeModules* modules,
+ SourceLineResolverInterface *resolver) {
+ // Find stack range.
+ int word_length = 0;
+ uint64_t stack_begin = 0, stack_end = 0;
+ if (cpu == "x86") {
+ word_length = 4;
+ const StackFrameX86 *frame_x86 = static_cast<const StackFrameX86*>(frame);
+ const StackFrameX86 *prev_frame_x86 =
+ static_cast<const StackFrameX86*>(prev_frame);
+ if ((frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) &&
+ (prev_frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)) {
+ stack_begin = frame_x86->context.esp;
+ stack_end = prev_frame_x86->context.esp;
+ }
+ } else if (cpu == "amd64") {
+ word_length = 8;
+ const StackFrameAMD64 *frame_amd64 =
+ static_cast<const StackFrameAMD64*>(frame);
+ const StackFrameAMD64 *prev_frame_amd64 =
+ static_cast<const StackFrameAMD64*>(prev_frame);
+ if ((frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) &&
+ (prev_frame_amd64->context_validity &
+ StackFrameAMD64::CONTEXT_VALID_RSP)) {
+ stack_begin = frame_amd64->context.rsp;
+ stack_end = prev_frame_amd64->context.rsp;
+ }
+ } else if (cpu == "arm") {
+ word_length = 4;
+ const StackFrameARM *frame_arm = static_cast<const StackFrameARM*>(frame);
+ const StackFrameARM *prev_frame_arm =
+ static_cast<const StackFrameARM*>(prev_frame);
+ if ((frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) &&
+ (prev_frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
+ stack_begin = frame_arm->context.iregs[13];
+ stack_end = prev_frame_arm->context.iregs[13];
+ }
+ } else if (cpu == "arm64") {
+ word_length = 8;
+ const StackFrameARM64 *frame_arm64 =
+ static_cast<const StackFrameARM64*>(frame);
+ const StackFrameARM64 *prev_frame_arm64 =
+ static_cast<const StackFrameARM64*>(prev_frame);
+ if ((frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) &&
+ (prev_frame_arm64->context_validity &
+ StackFrameARM64::CONTEXT_VALID_SP)) {
+ stack_begin = frame_arm64->context.iregs[31];
+ stack_end = prev_frame_arm64->context.iregs[31];
+ }
+ }
+ if (!word_length || !stack_begin || !stack_end)
+ return;
+
+ // Print stack contents.
+ printf("\n%sStack contents:", indent.c_str());
+ for(uint64_t address = stack_begin; address < stack_end; ) {
+ // Print the start address of this row.
+ if (word_length == 4)
+ printf("\n%s %08x", indent.c_str(), static_cast<uint32_t>(address));
+ else
+ printf("\n%s %016" PRIx64, indent.c_str(), address);
+
+ // Print data in hex.
+ const int kBytesPerRow = 16;
+ string data_as_string;
+ for (int i = 0; i < kBytesPerRow; ++i, ++address) {
+ uint8_t value = 0;
+ if (address < stack_end &&
+ memory->GetMemoryAtAddress(address, &value)) {
+ printf(" %02x", value);
+ data_as_string.push_back(isprint(value) ? value : '.');
+ } else {
+ printf(" ");
+ data_as_string.push_back(' ');
+ }
+ }
+ // Print data as string.
+ printf(" %s", data_as_string.c_str());
+ }
+
+ // Try to find instruction pointers from stack.
+ printf("\n%sPossible instruction pointers:\n", indent.c_str());
+ for (uint64_t address = stack_begin; address < stack_end;
+ address += word_length) {
+ StackFrame pointee_frame;
+
+ // Read a word (possible instruction pointer) from stack.
+ if (word_length == 4) {
+ uint32_t data32 = 0;
+ memory->GetMemoryAtAddress(address, &data32);
+ pointee_frame.instruction = data32;
+ } else {
+ uint64_t data64 = 0;
+ memory->GetMemoryAtAddress(address, &data64);
+ pointee_frame.instruction = data64;
+ }
+ pointee_frame.module =
+ modules->GetModuleForAddress(pointee_frame.instruction);
+
+ // Try to look up the function name.
+ if (pointee_frame.module)
+ resolver->FillSourceLineInfo(&pointee_frame);
+
+ // Print function name.
+ if (!pointee_frame.function_name.empty()) {
+ if (word_length == 4) {
+ printf("%s *(0x%08x) = 0x%08x", indent.c_str(),
+ static_cast<uint32_t>(address),
+ static_cast<uint32_t>(pointee_frame.instruction));
+ } else {
+ printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64,
+ indent.c_str(), address, pointee_frame.instruction);
+ }
+ printf(" <%s> [%s : %d + 0x%" PRIx64 "]\n",
+ pointee_frame.function_name.c_str(),
+ PathnameStripper::File(pointee_frame.source_file_name).c_str(),
+ pointee_frame.source_line,
+ pointee_frame.instruction - pointee_frame.source_line_base);
+ }
+ }
+ printf("\n");
+}
+
+// PrintStack prints the call stack in |stack| to stdout, in a reasonably
+// useful form. Module, function, and source file names are displayed if
+// they are available. The code offset to the base code address of the
+// source line, function, or module is printed, preferring them in that
+// order. If no source line, function, or module information is available,
+// an absolute code offset is printed.
+//
+// If |cpu| is a recognized CPU name, relevant register state for each stack
+// frame printed is also output, if available.
+static void PrintStack(const CallStack *stack,
+ const string &cpu,
+ bool output_stack_contents,
+ const MemoryRegion* memory,
+ const CodeModules* modules,
+ SourceLineResolverInterface* resolver) {
+ int frame_count = stack->frames()->size();
+ if (frame_count == 0) {
+ printf(" <no frames>\n");
+ }
+ for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
+ const StackFrame *frame = stack->frames()->at(frame_index);
+ printf("%2d ", frame_index);
+
+ uint64_t instruction_address = frame->ReturnAddress();
+
+ if (frame->module) {
+ printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
+ if (!frame->function_name.empty()) {
+ printf("!%s", frame->function_name.c_str());
+ if (!frame->source_file_name.empty()) {
+ string source_file = PathnameStripper::File(frame->source_file_name);
+ printf(" [%s : %d + 0x%" PRIx64 "]",
+ source_file.c_str(),
+ frame->source_line,
+ instruction_address - frame->source_line_base);
+ } else {
+ printf(" + 0x%" PRIx64, instruction_address - frame->function_base);
+ }
+ } else {
+ printf(" + 0x%" PRIx64,
+ instruction_address - frame->module->base_address());
+ }
+ } else {
+ printf("0x%" PRIx64, instruction_address);
+ }
+ printf("\n ");
+
+ int sequence = 0;
+ if (cpu == "x86") {
+ const StackFrameX86 *frame_x86 =
+ reinterpret_cast<const StackFrameX86*>(frame);
+
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
+ sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
+ sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
+ sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
+ sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
+ sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
+ sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
+ if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
+ sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
+ sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
+ sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
+ sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
+ }
+ } else if (cpu == "ppc") {
+ const StackFramePPC *frame_ppc =
+ reinterpret_cast<const StackFramePPC*>(frame);
+
+ if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
+ sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
+ if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
+ sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
+ } else if (cpu == "amd64") {
+ const StackFrameAMD64 *frame_amd64 =
+ reinterpret_cast<const StackFrameAMD64*>(frame);
+
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX)
+ sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX)
+ sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX)
+ sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
+ sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI)
+ sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI)
+ sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
+ sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
+ sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8)
+ sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9)
+ sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10)
+ sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11)
+ sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
+ sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
+ sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
+ sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
+ sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
+ sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
+ } else if (cpu == "sparc") {
+ const StackFrameSPARC *frame_sparc =
+ reinterpret_cast<const StackFrameSPARC*>(frame);
+
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
+ sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
+ sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
+ sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
+ } else if (cpu == "arm") {
+ const StackFrameARM *frame_arm =
+ reinterpret_cast<const StackFrameARM*>(frame);
+
+ // Argument registers (caller-saves), which will likely only be valid
+ // for the youngest frame.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
+ sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
+ sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
+ sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
+ sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
+
+ // General-purpose callee-saves registers.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
+ sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
+ sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
+ sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
+ sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
+ sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
+ sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
+ sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12)
+ sequence = PrintRegister("r12", frame_arm->context.iregs[12], sequence);
+
+ // Registers with a dedicated or conventional purpose.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
+ sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
+ sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
+ sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
+ sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
+ } else if (cpu == "arm64") {
+ const StackFrameARM64 *frame_arm64 =
+ reinterpret_cast<const StackFrameARM64*>(frame);
+
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
+ sequence =
+ PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
+ sequence =
+ PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
+ sequence =
+ PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
+ sequence =
+ PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
+ sequence =
+ PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
+ sequence =
+ PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
+ sequence =
+ PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
+ sequence =
+ PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
+ sequence =
+ PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
+ sequence =
+ PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
+ sequence =
+ PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
+ sequence =
+ PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
+ sequence =
+ PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
+ sequence =
+ PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
+ sequence =
+ PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
+ sequence =
+ PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
+ sequence =
+ PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
+ sequence =
+ PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
+ sequence =
+ PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
+ sequence =
+ PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
+ sequence =
+ PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
+ sequence =
+ PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
+ sequence =
+ PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
+ sequence =
+ PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
+ sequence =
+ PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
+ sequence =
+ PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
+ sequence =
+ PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
+ sequence =
+ PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
+ sequence =
+ PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
+ }
+
+ // Registers with a dedicated or conventional purpose.
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
+ sequence =
+ PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
+ sequence =
+ PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
+ sequence =
+ PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
+ sequence =
+ PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
+ }
+ } else if ((cpu == "mips") || (cpu == "mips64")) {
+ const StackFrameMIPS* frame_mips =
+ reinterpret_cast<const StackFrameMIPS*>(frame);
+
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
+ sequence = PrintRegister64("gp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
+ sequence = PrintRegister64("sp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
+ sequence = PrintRegister64("fp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
+ sequence = PrintRegister64("ra",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
+ sequence = PrintRegister64("pc", frame_mips->context.epc, sequence);
+
+ // Save registers s0-s7
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
+ sequence = PrintRegister64("s0",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
+ sequence = PrintRegister64("s1",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
+ sequence = PrintRegister64("s2",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
+ sequence = PrintRegister64("s3",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
+ sequence = PrintRegister64("s4",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
+ sequence = PrintRegister64("s5",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
+ sequence = PrintRegister64("s6",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
+ sequence = PrintRegister64("s7",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
+ sequence);
+ }
+ printf("\n Found by: %s\n", frame->trust_description().c_str());
+
+ // Print stack contents.
+ if (output_stack_contents && frame_index + 1 < frame_count) {
+ const string indent(" ");
+ PrintStackContents(indent, frame, stack->frames()->at(frame_index + 1),
+ cpu, memory, modules, resolver);
+ }
+ }
+}
+
+// PrintStackMachineReadable prints the call stack in |stack| to stdout,
+// in the following machine readable pipe-delimited text format:
+// thread number|frame number|module|function|source file|line|offset
+//
+// Module, function, source file, and source line may all be empty
+// depending on availability. The code offset follows the same rules as
+// PrintStack above.
+static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
+ int frame_count = stack->frames()->size();
+ for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
+ const StackFrame *frame = stack->frames()->at(frame_index);
+ printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
+ kOutputSeparator);
+
+ uint64_t instruction_address = frame->ReturnAddress();
+
+ if (frame->module) {
+ assert(!frame->module->code_file().empty());
+ printf("%s", StripSeparator(PathnameStripper::File(
+ frame->module->code_file())).c_str());
+ if (!frame->function_name.empty()) {
+ printf("%c%s", kOutputSeparator,
+ StripSeparator(frame->function_name).c_str());
+ if (!frame->source_file_name.empty()) {
+ printf("%c%s%c%d%c0x%" PRIx64,
+ kOutputSeparator,
+ StripSeparator(frame->source_file_name).c_str(),
+ kOutputSeparator,
+ frame->source_line,
+ kOutputSeparator,
+ instruction_address - frame->source_line_base);
+ } else {
+ printf("%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address - frame->function_base);
+ }
+ } else {
+ printf("%c%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address - frame->module->base_address());
+ }
+ } else {
+ // the printf before this prints a trailing separator for module name
+ printf("%c%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address);
+ }
+ printf("\n");
+ }
+}
+
+// ContainsModule checks whether a given |module| is in the vector
+// |modules_without_symbols|.
+static bool ContainsModule(
+ const vector<const CodeModule*> *modules,
+ const CodeModule *module) {
+ assert(modules);
+ assert(module);
+ vector<const CodeModule*>::const_iterator iter;
+ for (iter = modules->begin(); iter != modules->end(); ++iter) {
+ if (module->debug_file().compare((*iter)->debug_file()) == 0 &&
+ module->debug_identifier().compare((*iter)->debug_identifier()) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// PrintModule prints a single |module| to stdout.
+// |modules_without_symbols| should contain the list of modules that were
+// confirmed to be missing their symbols during the stack walk.
+static void PrintModule(
+ const CodeModule *module,
+ const vector<const CodeModule*> *modules_without_symbols,
+ const vector<const CodeModule*> *modules_with_corrupt_symbols,
+ uint64_t main_address) {
+ string symbol_issues;
+ if (ContainsModule(modules_without_symbols, module)) {
+ symbol_issues = " (WARNING: No symbols, " +
+ PathnameStripper::File(module->debug_file()) + ", " +
+ module->debug_identifier() + ")";
+ } else if (ContainsModule(modules_with_corrupt_symbols, module)) {
+ symbol_issues = " (WARNING: Corrupt symbols, " +
+ PathnameStripper::File(module->debug_file()) + ", " +
+ module->debug_identifier() + ")";
+ }
+ uint64_t base_address = module->base_address();
+ printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
+ base_address, base_address + module->size() - 1,
+ PathnameStripper::File(module->code_file()).c_str(),
+ module->version().empty() ? "???" : module->version().c_str(),
+ main_address != 0 && base_address == main_address ? " (main)" : "",
+ symbol_issues.c_str());
+}
+
+// PrintModules prints the list of all loaded |modules| to stdout.
+// |modules_without_symbols| should contain the list of modules that were
+// confirmed to be missing their symbols during the stack walk.
+static void PrintModules(
+ const CodeModules *modules,
+ const vector<const CodeModule*> *modules_without_symbols,
+ const vector<const CodeModule*> *modules_with_corrupt_symbols) {
+ if (!modules)
+ return;
+
+ printf("\n");
+ printf("Loaded modules:\n");
+
+ uint64_t main_address = 0;
+ const CodeModule *main_module = modules->GetMainModule();
+ if (main_module) {
+ main_address = main_module->base_address();
+ }
+
+ unsigned int module_count = modules->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < module_count;
+ ++module_sequence) {
+ const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
+ PrintModule(module, modules_without_symbols, modules_with_corrupt_symbols,
+ main_address);
+ }
+}
+
+// PrintModulesMachineReadable outputs a list of loaded modules,
+// one per line, in the following machine-readable pipe-delimited
+// text format:
+// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
+// {Base Address}|{Max Address}|{Main}
+static void PrintModulesMachineReadable(const CodeModules *modules) {
+ if (!modules)
+ return;
+
+ uint64_t main_address = 0;
+ const CodeModule *main_module = modules->GetMainModule();
+ if (main_module) {
+ main_address = main_module->base_address();
+ }
+
+ unsigned int module_count = modules->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < module_count;
+ ++module_sequence) {
+ const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
+ uint64_t base_address = module->base_address();
+ printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
+ kOutputSeparator,
+ StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
+ kOutputSeparator, StripSeparator(module->version()).c_str(),
+ kOutputSeparator,
+ StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
+ kOutputSeparator,
+ StripSeparator(module->debug_identifier()).c_str(),
+ kOutputSeparator, base_address,
+ kOutputSeparator, base_address + module->size() - 1,
+ kOutputSeparator,
+ main_module != NULL && base_address == main_address ? 1 : 0);
+ }
+}
+
+} // namespace
+
+void PrintProcessState(const ProcessState& process_state,
+ bool output_stack_contents,
+ SourceLineResolverInterface* resolver) {
+ // Print OS and CPU information.
+ string cpu = process_state.system_info()->cpu;
+ string cpu_info = process_state.system_info()->cpu_info;
+ printf("Operating system: %s\n", process_state.system_info()->os.c_str());
+ printf(" %s\n",
+ process_state.system_info()->os_version.c_str());
+ printf("CPU: %s\n", cpu.c_str());
+ if (!cpu_info.empty()) {
+ // This field is optional.
+ printf(" %s\n", cpu_info.c_str());
+ }
+ printf(" %d CPU%s\n",
+ process_state.system_info()->cpu_count,
+ process_state.system_info()->cpu_count != 1 ? "s" : "");
+ printf("\n");
+
+ // Print GPU information
+ string gl_version = process_state.system_info()->gl_version;
+ string gl_vendor = process_state.system_info()->gl_vendor;
+ string gl_renderer = process_state.system_info()->gl_renderer;
+ printf("GPU:");
+ if (!gl_version.empty() || !gl_vendor.empty() || !gl_renderer.empty()) {
+ printf(" %s\n", gl_version.c_str());
+ printf(" %s\n", gl_vendor.c_str());
+ printf(" %s\n", gl_renderer.c_str());
+ } else {
+ printf(" UNKNOWN\n");
+ }
+ printf("\n");
+
+ // Print crash information.
+ if (process_state.crashed()) {
+ printf("Crash reason: %s\n", process_state.crash_reason().c_str());
+ printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
+ } else {
+ printf("No crash\n");
+ }
+
+ string assertion = process_state.assertion();
+ if (!assertion.empty()) {
+ printf("Assertion: %s\n", assertion.c_str());
+ }
+
+ // Compute process uptime if the process creation and crash times are
+ // available in the dump.
+ if (process_state.time_date_stamp() != 0 &&
+ process_state.process_create_time() != 0 &&
+ process_state.time_date_stamp() >= process_state.process_create_time()) {
+ printf("Process uptime: %d seconds\n",
+ process_state.time_date_stamp() -
+ process_state.process_create_time());
+ } else {
+ printf("Process uptime: not available\n");
+ }
+
+ // If the thread that requested the dump is known, print it first.
+ int requesting_thread = process_state.requesting_thread();
+ if (requesting_thread != -1) {
+ printf("\n");
+ printf("Thread %d (%s)\n",
+ requesting_thread,
+ process_state.crashed() ? "crashed" :
+ "requested dump, did not crash");
+ PrintStack(process_state.threads()->at(requesting_thread), cpu,
+ output_stack_contents,
+ process_state.thread_memory_regions()->at(requesting_thread),
+ process_state.modules(), resolver);
+ }
+
+ // Print all of the threads in the dump.
+ int thread_count = process_state.threads()->size();
+ for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
+ if (thread_index != requesting_thread) {
+ // Don't print the crash thread again, it was already printed.
+ printf("\n");
+ printf("Thread %d\n", thread_index);
+ PrintStack(process_state.threads()->at(thread_index), cpu,
+ output_stack_contents,
+ process_state.thread_memory_regions()->at(thread_index),
+ process_state.modules(), resolver);
+ }
+ }
+
+ PrintModules(process_state.modules(),
+ process_state.modules_without_symbols(),
+ process_state.modules_with_corrupt_symbols());
+}
+
+void PrintProcessStateMachineReadable(const ProcessState& process_state) {
+ // Print OS and CPU information.
+ // OS|{OS Name}|{OS Version}
+ // CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
+ // GPU|{GPU version}|{GPU vendor}|{GPU renderer}
+ printf("OS%c%s%c%s\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->os).c_str(),
+ kOutputSeparator,
+ StripSeparator(process_state.system_info()->os_version).c_str());
+ printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->cpu).c_str(),
+ kOutputSeparator,
+ // this may be empty
+ StripSeparator(process_state.system_info()->cpu_info).c_str(),
+ kOutputSeparator,
+ process_state.system_info()->cpu_count);
+ printf("GPU%c%s%c%s%c%s\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->gl_version).c_str(),
+ kOutputSeparator,
+ StripSeparator(process_state.system_info()->gl_vendor).c_str(),
+ kOutputSeparator,
+ StripSeparator(process_state.system_info()->gl_renderer).c_str());
+
+ int requesting_thread = process_state.requesting_thread();
+
+ // Print crash information.
+ // Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
+ printf("Crash%c", kOutputSeparator);
+ if (process_state.crashed()) {
+ printf("%s%c0x%" PRIx64 "%c",
+ StripSeparator(process_state.crash_reason()).c_str(),
+ kOutputSeparator, process_state.crash_address(), kOutputSeparator);
+ } else {
+ // print assertion info, if available, in place of crash reason,
+ // instead of the unhelpful "No crash"
+ string assertion = process_state.assertion();
+ if (!assertion.empty()) {
+ printf("%s%c%c", StripSeparator(assertion).c_str(),
+ kOutputSeparator, kOutputSeparator);
+ } else {
+ printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
+ }
+ }
+
+ if (requesting_thread != -1) {
+ printf("%d\n", requesting_thread);
+ } else {
+ printf("\n");
+ }
+
+ PrintModulesMachineReadable(process_state.modules());
+
+ // blank line to indicate start of threads
+ printf("\n");
+
+ // If the thread that requested the dump is known, print it first.
+ if (requesting_thread != -1) {
+ PrintStackMachineReadable(requesting_thread,
+ process_state.threads()->at(requesting_thread));
+ }
+
+ // Print all of the threads in the dump.
+ int thread_count = process_state.threads()->size();
+ for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
+ if (thread_index != requesting_thread) {
+ // Don't print the crash thread again, it was already printed.
+ PrintStackMachineReadable(thread_index,
+ process_state.threads()->at(thread_index));
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.h
new file mode 100644
index 000000000..a74f7b6da
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalk_common.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// 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,
+ SourceLineResolverInterface* resolver);
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STACKWALK_COMMON_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
new file mode 100644
index 000000000..98cb0b5be
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker.cc: Generic stackwalker.
+//
+// See stackwalker.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "google_breakpad/processor/stackwalker.h"
+
+#include <assert.h>
+
+#include "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"
+
+namespace google_breakpad {
+
+const int Stackwalker::kRASearchWords = 40;
+
+uint32_t Stackwalker::max_frames_ = 1024;
+bool Stackwalker::max_frames_set_ = false;
+
+uint32_t Stackwalker::max_frames_scanned_ = 1024;
+
+Stackwalker::Stackwalker(const SystemInfo* system_info,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer)
+ : system_info_(system_info),
+ memory_(memory),
+ modules_(modules),
+ 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).
+
+ // Resolve the module information, if a module map was provided.
+ StackFrameSymbolizer::SymbolizerResult symbolizer_result =
+ frame_symbolizer_->FillSourceLineInfo(modules_, system_info_,
+ frame.get());
+ 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 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,
+ 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;
+ }
+
+ BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
+ ", can't choose a stackwalker "
+ "implementation";
+ return cpu_stackwalker;
+}
+
+bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) {
+ StackFrame frame;
+ frame.instruction = address;
+ StackFrameSymbolizer::SymbolizerResult symbolizer_result =
+ frame_symbolizer_->FillSourceLineInfo(modules_, system_info_, &frame);
+
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.cc
new file mode 100644
index 000000000..e81fec282
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_address_list.cc: a pseudo stack walker.
+//
+// See stackwalker_address_list.h for documentation.
+//
+// Author: Chris Hamilton <chrisha@chromium.org>
+
+#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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.h
new file mode 100644
index 000000000..0f8c989ef
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_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 "common/basictypes.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);
+
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackwalkerAddressList);
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STACKWALKER_ADDRESS_LIST_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list_unittest.cc
new file mode 100644
index 000000000..ab4e9c088
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_address_list_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2013, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_address_list_unittest.cc: Unit tests for the
+// StackwalkerAddressList class.
+//
+// Author: Chris Hamilton <chrisha@chromium.org>
+
+#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::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);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc
new file mode 100644
index 000000000..440724a1e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_amd64.cc: amd64-specific stackwalker.
+//
+// See stackwalker_amd64.h for documentation.
+//
+// Author: Mark Mentovai, Ted Mielczarek
+
+#include <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;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+ return frame.release();
+}
+
+bool StackwalkerAMD64::IsEndOfStack(uint64_t caller_rip, uint64_t caller_rsp,
+ uint64_t callee_rsp) {
+ // Treat an instruction address of 0 as end-of-stack.
+ if (caller_rip == 0) {
+ return true;
+ }
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (caller_rsp < callee_rsp) {
+ return true;
+ }
+
+ return false;
+}
+
+// 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_rsp = last_frame->context.rsp;
+ 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;
+ }
+
+ // Simple sanity check that the stack is growing downwards as expected.
+ if (IsEndOfStack(caller_rip, caller_rsp, last_rsp) ||
+ caller_rbp < last_rbp) {
+ // Reached end-of-stack or stack is not growing downwards.
+ 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::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,
+ frames.size() == 1 /* is_context_frame */)) {
+ // 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 DWARF 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 or failed, try using frame pointer recovery.
+ if (!new_frame.get()) {
+ 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);
+ }
+
+ if (IsEndOfStack(new_frame->context.rip, new_frame->context.rsp,
+ last_frame->context.rsp)) {
+ // Reached end-of-stack.
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h
new file mode 100644
index 000000000..67c455104
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_amd64.h: amd64-specific stackwalker.
+//
+// Provides stack frames given amd64 register context and a memory region
+// corresponding to a amd64 stack.
+//
+// Author: Mark Mentovai, Ted Mielczarek
+
+
+#ifndef PROCESSOR_STACKWALKER_AMD64_H__
+#define PROCESSOR_STACKWALKER_AMD64_H__
+
+#include <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);
+
+ // Checks whether end-of-stack is reached. An instruction address of 0 is an
+ // end-of-stack marker. If the stack pointer of the caller is at a lower
+ // address than the stack pointer of the callee, then that's clearly incorrect
+ // and it is treated as end-of-stack to enforce progress and avoid infinite
+ // loops.
+ bool IsEndOfStack(uint64_t caller_rip, uint64_t caller_rsp,
+ uint64_t callee_rsp);
+
+ // 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);
+
+ // 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc
new file mode 100644
index 000000000..935bef866
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc
@@ -0,0 +1,932 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class.
+
+#include <string.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::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(&not_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(&not_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
+ 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();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc
new file mode 100644
index 000000000..e4fc58697
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_arm.cc: arm-specific stackwalker.
+//
+// See stackwalker_arm.h for documentation.
+//
+// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
+
+#include <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_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,
+ frames.size() == 1 /* is_context_frame */)) {
+ // 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.
+ 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;
+
+
+ // An instruction address of zero marks the end of the stack.
+ if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0)
+ return NULL;
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP]
+ < last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP])
+ return NULL;
+
+ // The new frame's context's PC is the return address, which is one
+ // instruction past the instruction that caused us to arrive at the
+ // callee. Set new_frame->instruction to one less than the PC. This won't
+ // reference the beginning of the call instruction, but it's at least
+ // within it, which is sufficient to get the source line information to
+ // match up with the line that contains the function call. Callers that
+ // require the exact return address value may access
+ // frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
+ frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 2;
+
+ return frame.release();
+}
+
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h
new file mode 100644
index 000000000..9081a40cd
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h
@@ -0,0 +1,107 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_arm.h: arm-specific stackwalker.
+//
+// Provides stack frames given arm register context and a memory region
+// corresponding to an arm stack.
+//
+// Author: Mark Mentovai, Ted Mielczarek
+
+
+#ifndef PROCESSOR_STACKWALKER_ARM_H__
+#define PROCESSOR_STACKWALKER_ARM_H__
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stackwalker.h"
+
+namespace google_breakpad {
+
+class CodeModules;
+
+class StackwalkerARM : public Stackwalker {
+ public:
+ // context is an arm context object that gives access to arm-specific
+ // register state corresponding to the innermost called frame to be
+ // included in the stack. The other arguments are passed directly through
+ // to the base Stackwalker constructor.
+ StackwalkerARM(const SystemInfo* system_info,
+ const MDRawContextARM* context,
+ 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.cc
new file mode 100644
index 000000000..31119a97e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_arm64.cc: arm64-specific stackwalker.
+//
+// See stackwalker_arm64.h for documentation.
+//
+// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy, Colin Blundell
+
+#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) { }
+
+
+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];
+
+ 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->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,
+ frames.size() == 1 /* is_context_frame */)) {
+ // 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());
+
+ 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;
+ }
+
+ 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;
+}
+
+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;
+
+ // An instruction address of zero marks the end of the stack.
+ if (frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] == 0)
+ return NULL;
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (frame->context.iregs[MD_CONTEXT_ARM64_REG_SP]
+ < last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP])
+ return NULL;
+
+ // The new frame's context's PC is the return address, which is one
+ // instruction past the instruction that caused us to arrive at the callee.
+ // 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
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.h
new file mode 100644
index 000000000..121e82467
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64.h
@@ -0,0 +1,104 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_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:
+ // 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);
+
+ // 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_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STACKWALKER_ARM64_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64_unittest.cc
new file mode 100644
index 000000000..f9d18cea0
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm64_unittest.cc
@@ -0,0 +1,880 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_arm64_unittest.cc: Unit tests for StackwalkerARM64 class.
+
+#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_arm64.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameARM64;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerARM64;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerARM64Fixture {
+ public:
+ StackwalkerARM64Fixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x40000000, 0x10000, "module1", "version1"),
+ module2(0x50000000, 0x10000, "module2", "version2") {
+ // Identify the system as an iOS system.
+ system_info.os = "iOS";
+ system_info.os_short = "ios";
+ system_info.cpu = "arm64";
+ 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(MDRawContextARM64 *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;
+ MDRawContextARM64 raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame *> *frames;
+};
+
+class SanityCheck: public StackwalkerARM64Fixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ // Since the context's frame pointer is garbage, the stack walk will end after
+ // the first frame.
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerARM64 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(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetContextFrame: public StackwalkerARM64Fixture, public Test { };
+
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(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 StackwalkerARM64Fixture, public Test { };
+
+TEST_F(GetCallerFrame, ScanWithoutSymbols) {
+ // When the stack walker resorts to scanning the stack,
+ // only addresses located within loaded modules are
+ // considered valid return addresses.
+ // Force scanning through three frames to ensure that the
+ // stack pointer is set properly in scan-recovered frames.
+ stack_section.start() = 0x80000000;
+ uint64_t return_address1 = 0x50000100;
+ uint64_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D64(0x40090000) // junk that's not
+ .D64(0x60000000) // a return address
+
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(16, 0) // space
+
+ .D64(0xF0000000) // more junk
+ .D64(0x0000000D)
+
+ .D64(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
+ frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+
+ StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+}
+
+TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
+ // During stack scanning, if a potential return address
+ // is located within a loaded module that has symbols,
+ // it is only considered a valid return address if it
+ // lies within a function's bounds.
+ stack_section.start() = 0x80000000;
+ uint64_t return_address = 0x50000200;
+ Label frame1_sp;
+
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D64(0x40090000) // junk that's not
+ .D64(0x60000000) // a return address
+
+ .D64(0x40001000) // a couple of plausible addresses
+ .D64(0x5000F000) // that are not within functions
+
+ .D64(return_address) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40000200;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 100 400 10 monotreme\n");
+ SetModuleSymbols(&module2,
+ // The calling frame's function.
+ "FUNC 100 400 10 marsupial\n");
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
+ frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+ EXPECT_EQ("monotreme", frame0->function_name);
+ EXPECT_EQ(0x40000100ULL, frame0->function_base);
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+ EXPECT_EQ("marsupial", frame1->function_name);
+ EXPECT_EQ(0x50000100ULL, frame1->function_base);
+}
+
+TEST_F(GetCallerFrame, ScanFirstFrame) {
+ // If the stackwalker resorts to stack scanning, it will scan much
+ // farther to find the caller of the context frame.
+ stack_section.start() = 0x80000000;
+ uint64_t return_address1 = 0x50000100;
+ uint64_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D64(0x40090000) // junk that's not
+ .D64(0x60000000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(32, 0) // space
+
+ .D64(0xF0000000) // more junk
+ .D64(0x0000000D)
+
+ .Append(336, 0) // more space
+
+ .D64(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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(2U, frames->size());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
+ frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+}
+
+// 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() = 0x80000000;
+ uint64_t return_address1 = 0x50000100;
+ uint64_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D64(0x40090000) // junk that's not
+ .D64(0x60000000) // a return address
+
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(16, 0) // space
+
+ .D64(0xF0000000) // more junk
+ .D64(0x0000000D)
+
+ .D64(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
+ frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+}
+
+class GetFramesByFramePointer: public StackwalkerARM64Fixture, public Test { };
+
+TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
+ stack_section.start() = 0x80000000;
+ uint64_t return_address1 = 0x50000100;
+ uint64_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ Label frame1_fp, frame2_fp;
+ stack_section
+ // frame 0
+ .Append(64, 0) // Whatever values on the stack.
+ .D64(0x0000000D) // junk that's not
+ .D64(0xF0000000) // a return address.
+
+ .Mark(&frame1_fp) // Next fp will point to the next value.
+ .D64(frame2_fp) // Save current frame pointer.
+ .D64(return_address2) // Save current link register.
+ .Mark(&frame1_sp)
+
+ // frame 1
+ .Append(64, 0) // Whatever values on the stack.
+ .D64(0x0000000D) // junk that's not
+ .D64(0xF0000000) // a return address.
+
+ .Mark(&frame2_fp)
+ .D64(0)
+ .D64(0)
+ .Mark(&frame2_sp)
+
+ // frame 2
+ .Append(64, 0) // Whatever values on the stack.
+ .D64(0x0000000D) // junk that's not
+ .D64(0xF0000000); // a return address.
+ RegionFromSection();
+
+
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = return_address1;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = frame1_fp.Value();
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
+ frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_LR |
+ StackFrameARM64::CONTEXT_VALID_FP |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+ EXPECT_EQ(frame2_fp.Value(),
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
+
+ StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
+ ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_LR |
+ StackFrameARM64::CONTEXT_VALID_FP |
+ StackFrameARM64::CONTEXT_VALID_SP),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
+}
+
+struct CFIFixture: public StackwalkerARM64Fixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; we'll walk to the caller
+ // from every point in this series, expecting to find the same set
+ // of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the link
+ // register (x30).
+ "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: x30\n"
+ // Push x19, x20, the frame pointer and the link register.
+ "STACK CFI 4001 .cfa: sp 32 + .ra: .cfa -8 + ^"
+ " x19: .cfa -32 + ^ x20: .cfa -24 + ^ "
+ " x29: .cfa -16 + ^\n"
+ // Save x19..x22 in x0..x3: verify that we populate
+ // the youngest frame with all the values we have.
+ "STACK CFI 4002 x19: x0 x20: x1 x21: x2 x22: x3\n"
+ // Restore x19..x22. Save the non-callee-saves register x1.
+ "STACK CFI 4003 .cfa: sp 40 + x1: .cfa 40 - ^"
+ " x19: x19 x20: x20 x21: x21 x22: x22\n"
+ // Move the .cfa back eight bytes, to point at the return
+ // address, and restore the sp explicitly.
+ "STACK CFI 4005 .cfa: sp 32 + x1: .cfa 32 - ^"
+ " x29: .cfa 8 - ^ .ra: .cfa ^ sp: .cfa 8 +\n"
+ // Recover the PC explicitly from a new stack slot;
+ // provide garbage for the .ra.
+ "STACK CFI 4006 .cfa: sp 40 + pc: .cfa 40 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
+
+ // A function whose CFI makes the stack pointer
+ // go backwards.
+ "FUNC 6000 1000 20 palinal\n"
+ "STACK CFI INIT 6000 1000 .cfa: sp 8 - .ra: x30\n"
+
+ // A function with CFI expressions that can't be
+ // evaluated.
+ "FUNC 7000 1000 20 rhetorical\n"
+ "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040005510L;
+ expected.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ expected.iregs[19] = 0x5e68b5d5b5d55e68L;
+ expected.iregs[20] = 0x34f3ebd1ebd134f3L;
+ expected.iregs[21] = 0x74bca31ea31e74bcL;
+ expected.iregs[22] = 0x16b32dcb2dcb16b3L;
+ expected.iregs[23] = 0x21372ada2ada2137L;
+ expected.iregs[24] = 0x557dbbbbbbbb557dL;
+ expected.iregs[25] = 0x8ca748bf48bf8ca7L;
+ expected.iregs[26] = 0x21f0ab46ab4621f0L;
+ expected.iregs[27] = 0x146732b732b71467L;
+ expected.iregs[28] = 0xa673645fa673645fL;
+ expected.iregs[MD_CONTEXT_ARM64_REG_FP] = 0xe11081128112e110L;
+
+ // Expect CFI to recover all callee-saves registers. Since CFI is the
+ // only stack frame construction technique we have, aside from the
+ // context frame itself, there's no way for us to have a set of valid
+ // registers smaller than this.
+ expected_validity = (StackFrameARM64::CONTEXT_VALID_PC |
+ StackFrameARM64::CONTEXT_VALID_SP |
+ StackFrameARM64::CONTEXT_VALID_X19 |
+ StackFrameARM64::CONTEXT_VALID_X20 |
+ StackFrameARM64::CONTEXT_VALID_X21 |
+ StackFrameARM64::CONTEXT_VALID_X22 |
+ StackFrameARM64::CONTEXT_VALID_X23 |
+ StackFrameARM64::CONTEXT_VALID_X24 |
+ StackFrameARM64::CONTEXT_VALID_X25 |
+ StackFrameARM64::CONTEXT_VALID_X26 |
+ StackFrameARM64::CONTEXT_VALID_X27 |
+ StackFrameARM64::CONTEXT_VALID_X28 |
+ StackFrameARM64::CONTEXT_VALID_FP);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameARM64::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 walker(&system_info, &raw_context, &stack_region,
+ &modules, &frame_symbolizer);
+ walker.SetContextFrameValidity(context_frame_validity);
+ 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());
+
+ StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(context_frame_validity, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x0000000040004000UL, frame0->function_base);
+
+ StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X1)
+ EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X19)
+ EXPECT_EQ(expected.iregs[19], frame1->context.iregs[19]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X20)
+ EXPECT_EQ(expected.iregs[20], frame1->context.iregs[20]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X21)
+ EXPECT_EQ(expected.iregs[21], frame1->context.iregs[21]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X22)
+ EXPECT_EQ(expected.iregs[22], frame1->context.iregs[22]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X23)
+ EXPECT_EQ(expected.iregs[23], frame1->context.iregs[23]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X24)
+ EXPECT_EQ(expected.iregs[24], frame1->context.iregs[24]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X25)
+ EXPECT_EQ(expected.iregs[25], frame1->context.iregs[25]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X26)
+ EXPECT_EQ(expected.iregs[26], frame1->context.iregs[26]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X27)
+ EXPECT_EQ(expected.iregs[27], frame1->context.iregs[27]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_X28)
+ EXPECT_EQ(expected.iregs[28], frame1->context.iregs[28]);
+ if (expected_validity & StackFrameARM64::CONTEXT_VALID_FP)
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
+
+ // We would never have gotten a frame in the first place if the SP
+ // and PC weren't valid or ->instruction weren't set.
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_PC],
+ frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM64_REG_PC],
+ frame1->instruction + 4);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextARM64 expected;
+
+ // The validity mask for expected.
+ uint64_t expected_validity;
+
+ // The validity mask to impose on the context frame.
+ uint64_t context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ stack_section.start() = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = 0x0000000040005510L;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x5e68b5d5b5d55e68L) // saved x19
+ .D64(0x34f3ebd1ebd134f3L) // saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004001L;
+ // distinct callee x19, x20 and fp
+ raw_context.iregs[19] = 0xadc9f635a635adc9L;
+ raw_context.iregs[20] = 0x623135ac35ac6231L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ CheckWalk();
+}
+
+// As above, but unwind from a context that has only the PC and SP.
+TEST_F(CFI, At4001LimitedValidity) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x5e68b5d5b5d55e68L) // saved x19
+ .D64(0x34f3ebd1ebd134f3L) // saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ context_frame_validity =
+ StackFrameARM64::CONTEXT_VALID_PC | StackFrameARM64::CONTEXT_VALID_SP;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004001L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+
+ expected_validity = (StackFrameARM64::CONTEXT_VALID_PC
+ | StackFrameARM64::CONTEXT_VALID_SP
+ | StackFrameARM64::CONTEXT_VALID_FP
+ | StackFrameARM64::CONTEXT_VALID_X19
+ | StackFrameARM64::CONTEXT_VALID_X20);
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004002L;
+ raw_context.iregs[0] = 0x5e68b5d5b5d55e68L; // saved x19
+ raw_context.iregs[1] = 0x34f3ebd1ebd134f3L; // saved x20
+ raw_context.iregs[2] = 0x74bca31ea31e74bcL; // saved x21
+ raw_context.iregs[3] = 0x16b32dcb2dcb16b3L; // saved x22
+ raw_context.iregs[19] = 0xadc9f635a635adc9L; // distinct callee x19
+ raw_context.iregs[20] = 0x623135ac35ac6231L; // distinct callee x20
+ raw_context.iregs[21] = 0xac4543564356ac45L; // distinct callee x21
+ raw_context.iregs[22] = 0x2561562f562f2561L; // distinct callee x22
+ // distinct callee fp
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004003L;
+ // distinct callee x1 and fp
+ raw_context.iregs[1] = 0xfb756319fb756319L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ // caller's x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL;
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// We have no new rule at module offset 0x4004, so the results here should
+// be the same as those at module offset 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004004L;
+ // distinct callee x1 and fp
+ raw_context.iregs[1] = 0xfb756319fb756319L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = 0x5fc4be14be145fc4L;
+ // caller's x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL;
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Here we move the .cfa, but provide an explicit rule to recover the SP,
+// so again there should be no change in the registers recovered.
+TEST_F(CFI, At4005) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0x0000000040005510L) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004005L;
+ raw_context.iregs[1] = 0xfb756319fb756319L; // distinct callee x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL; // caller's x1
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Here we provide an explicit rule for the PC, and have the saved .ra be
+// bogus.
+TEST_F(CFI, At4006) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM64_REG_SP];
+ stack_section
+ .D64(0x0000000040005510L) // saved pc
+ .D64(0xdd5a48c848c8dd5aL) // saved x1 (even though it's not callee-saves)
+ .D64(0xff3dfb81fb81ff3dL) // no longer saved x19
+ .D64(0x34f3ebd1ebd134f3L) // no longer saved x20
+ .D64(0xe11081128112e110L) // saved fp
+ .D64(0xf8d157835783f8d1L) // .ra rule recovers this, which is garbage
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040004006L;
+ raw_context.iregs[1] = 0xfb756319fb756319L; // distinct callee x1
+ expected.iregs[1] = 0xdd5a48c848c8dd5aL; // caller's x1
+ expected_validity |= StackFrameARM64::CONTEXT_VALID_X1;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040006000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = 0x0000000040005510L;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x0000000040007000L;
+ raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = 0x0000000080000000L;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM64 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(1U, frames->size());
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc
new file mode 100644
index 000000000..8a0fd5e95
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc
@@ -0,0 +1,974 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class.
+
+#include <string.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_arm.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameARM;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerARM;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerARMFixture {
+ public:
+ StackwalkerARMFixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x40000000, 0x10000, "module1", "version1"),
+ module2(0x50000000, 0x10000, "module2", "version2") {
+ // Identify the system as a Linux system.
+ system_info.os = "Linux";
+ system_info.os_short = "linux";
+ system_info.os_version = "Lugubrious Labrador";
+ system_info.cpu = "arm";
+ system_info.cpu_info = "";
+
+ // Put distinctive values in the raw CPU context.
+ BrandContext(&raw_context);
+
+ // Create some modules with some stock debugging information.
+ modules.Add(&module1);
+ modules.Add(&module2);
+
+ // By default, none of the modules have symbol info; call
+ // SetModuleSymbols to override this.
+ EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _, _))
+ .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
+
+ // 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(MDRawContextARM *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;
+ MDRawContextARM raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame *> *frames;
+};
+
+class SanityCheck: public StackwalkerARMFixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ // Since we have no call frame information, and all unwinding
+ // requires call frame information, the stack walk will end after
+ // the first frame.
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetContextFrame: public StackwalkerARMFixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ // Since we have no call frame information, and all unwinding
+ // requires call frame information, the stack walk will end after
+ // the first frame.
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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(1U, frames->size());
+ StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, 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(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetCallerFrame: public StackwalkerARMFixture, public Test { };
+
+TEST_F(GetCallerFrame, ScanWithoutSymbols) {
+ // When the stack walker resorts to scanning the stack,
+ // only addresses located within loaded modules are
+ // considered valid return addresses.
+ // Force scanning through three frames to ensure that the
+ // stack pointer is set properly in scan-recovered frames.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x50000100;
+ uint32_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D32(0x40090000) // junk that's not
+ .D32(0x60000000) // a return address
+
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(16, 0) // space
+
+ .D32(0xF0000000) // more junk
+ .D32(0x0000000D)
+
+ .D32(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+
+ StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+}
+
+TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
+ // During stack scanning, if a potential return address
+ // is located within a loaded module that has symbols,
+ // it is only considered a valid return address if it
+ // lies within a function's bounds.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address = 0x50000200;
+ Label frame1_sp;
+
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D32(0x40090000) // junk that's not
+ .D32(0x60000000) // a return address
+
+ .D32(0x40001000) // a couple of plausible addresses
+ .D32(0x5000F000) // that are not within functions
+
+ .D32(return_address) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40000200;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 100 400 10 monotreme\n");
+ SetModuleSymbols(&module2,
+ // The calling frame's function.
+ "FUNC 100 400 10 marsupial\n");
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+ EXPECT_EQ("monotreme", frame0->function_name);
+ EXPECT_EQ(0x40000100U, frame0->function_base);
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ("marsupial", frame1->function_name);
+ EXPECT_EQ(0x50000100U, frame1->function_base);
+}
+
+TEST_F(GetCallerFrame, ScanFirstFrame) {
+ // If the stackwalker resorts to stack scanning, it will scan much
+ // farther to find the caller of the context frame.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x50000100;
+ uint32_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D32(0x40090000) // junk that's not
+ .D32(0x60000000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(32, 0) // space
+
+ .D32(0xF0000000) // more junk
+ .D32(0x0000000D)
+
+ .Append(136, 0) // more space
+
+ .D32(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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(2U, frames->size());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+}
+
+// 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() = 0x80000000;
+ uint32_t return_address1 = 0x50000100;
+ uint32_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D32(0x40090000) // junk that's not
+ .D32(0x60000000) // a return address
+
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(16, 0) // space
+
+ .D32(0xF0000000) // more junk
+ .D32(0x0000000D)
+
+ .D32(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+}
+
+struct CFIFixture: public StackwalkerARMFixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; we'll walk to the caller
+ // from every point in this series, expecting to find the same set
+ // of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the link register.
+ "STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n"
+ // Push r4, the frame pointer, and the link register.
+ "STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^"
+ " r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n"
+ // Save r4..r7 in r0..r3: verify that we populate
+ // the youngest frame with all the values we have.
+ "STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n"
+ // Restore r4..r7. Save the non-callee-saves register r1.
+ "STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^"
+ " r4: r4 r5: r5 r6: r6 r7: r7\n"
+ // Move the .cfa back four bytes, to point at the return
+ // address, and restore the sp explicitly.
+ "STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^"
+ " r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n"
+ // Recover the PC explicitly from a new stack slot;
+ // provide garbage for the .ra.
+ "STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
+
+ // A function whose CFI makes the stack pointer
+ // go backwards.
+ "FUNC 6000 1000 20 palinal\n"
+ "STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n"
+
+ // A function with CFI expressions that can't be
+ // evaluated.
+ "FUNC 7000 1000 20 rhetorical\n"
+ "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ expected.iregs[4] = 0xb5d55e68;
+ expected.iregs[5] = 0xebd134f3;
+ expected.iregs[6] = 0xa31e74bc;
+ expected.iregs[7] = 0x2dcb16b3;
+ expected.iregs[8] = 0x2ada2137;
+ expected.iregs[9] = 0xbbbb557d;
+ expected.iregs[10] = 0x48bf8ca7;
+ expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110;
+
+ // Expect CFI to recover all callee-saves registers. Since CFI is the
+ // only stack frame construction technique we have, aside from the
+ // context frame itself, there's no way for us to have a set of valid
+ // registers smaller than this.
+ expected_validity = (StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_SP |
+ StackFrameARM::CONTEXT_VALID_R4 |
+ StackFrameARM::CONTEXT_VALID_R5 |
+ StackFrameARM::CONTEXT_VALID_R6 |
+ StackFrameARM::CONTEXT_VALID_R7 |
+ StackFrameARM::CONTEXT_VALID_R8 |
+ StackFrameARM::CONTEXT_VALID_R9 |
+ StackFrameARM::CONTEXT_VALID_R10 |
+ StackFrameARM::CONTEXT_VALID_FP);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region,
+ &modules, &frame_symbolizer);
+ walker.SetContextFrameValidity(context_frame_validity);
+ 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());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(context_frame_validity, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x40004000U, frame0->function_base);
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R1)
+ EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R4)
+ EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R5)
+ EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R6)
+ EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R7)
+ EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R8)
+ EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R9)
+ EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_R10)
+ EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]);
+ if (expected_validity & StackFrameARM::CONTEXT_VALID_FP)
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]);
+
+ // We would never have gotten a frame in the first place if the SP
+ // and PC weren't valid or ->instruction weren't set.
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
+ frame1->instruction + 2);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextARM expected;
+
+ // The validity mask for expected.
+ int expected_validity;
+
+ // The validity mask to impose on the context frame.
+ int context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xb5d55e68) // saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
+ raw_context.iregs[4] = 0x635adc9f; // distinct callee r4
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ CheckWalk();
+}
+
+// As above, but unwind from a context that has only the PC and SP.
+TEST_F(CFI, At4001LimitedValidity) {
+ context_frame_validity =
+ StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xb5d55e68) // saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ expected_validity = (StackFrameARM::CONTEXT_VALID_PC
+ | StackFrameARM::CONTEXT_VALID_SP
+ | StackFrameARM::CONTEXT_VALID_FP
+ | StackFrameARM::CONTEXT_VALID_R4);
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0xfb81ff3d) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002;
+ raw_context.iregs[0] = 0xb5d55e68; // saved r4
+ raw_context.iregs[1] = 0xebd134f3; // saved r5
+ raw_context.iregs[2] = 0xa31e74bc; // saved r6
+ raw_context.iregs[3] = 0x2dcb16b3; // saved r7
+ raw_context.iregs[4] = 0xfdd35466; // distinct callee r4
+ raw_context.iregs[5] = 0xf18c946c; // distinct callee r5
+ raw_context.iregs[6] = 0xac2079e8; // distinct callee r6
+ raw_context.iregs[7] = 0xa449829f; // distinct callee r7
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xcb78040e) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// We have no new rule at module offset 0x4004, so the results here should
+// be the same as those at module offset 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xcb78040e) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Here we move the .cfa, but provide an explicit rule to recover the SP,
+// so again there should be no change in the registers recovered.
+TEST_F(CFI, At4005) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xf013f841) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0x40005510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005;
+ raw_context.iregs[1] = 0xfb756319; // distinct callee r1
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Here we provide an explicit rule for the PC, and have the saved .ra be
+// bogus.
+TEST_F(CFI, At4006) {
+ Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
+ stack_section
+ .D32(0x40005510) // saved pc
+ .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
+ .D32(0xf013f841) // no longer saved r4
+ .D32(0x8112e110) // saved fp
+ .D32(0xf8d15783) // .ra rule recovers this, which is garbage
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006;
+ raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's
+ expected.iregs[1] = 0x48c8dd5a; // caller's r1
+ expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, &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(1U, frames->size());
+}
+
+class StackwalkerARMFixtureIOS : public StackwalkerARMFixture {
+ public:
+ StackwalkerARMFixtureIOS() {
+ system_info.os = "iOS";
+ system_info.os_short = "ios";
+ }
+};
+
+class GetFramesByFramePointer: public StackwalkerARMFixtureIOS, public Test { };
+
+TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x50000100;
+ uint32_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ Label frame1_fp, frame2_fp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000) // a return address.
+
+ .Mark(&frame1_fp) // Next fp will point to the next value.
+ .D32(frame2_fp) // Save current frame pointer.
+ .D32(return_address2) // Save current link register.
+ .Mark(&frame1_sp)
+
+ // frame 1
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000) // a return address.
+
+ .Mark(&frame2_fp)
+ .D32(0)
+ .D32(0)
+ .Mark(&frame2_sp)
+
+ // frame 2
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000); // a return address.
+ RegionFromSection();
+
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value();
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP,
+ &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());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_LR |
+ StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(frame2_fp.Value(),
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
+
+ StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_LR |
+ StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
+}
+
+TEST_F(GetFramesByFramePointer, FramePointerAndCFI) {
+ // Provide the standatd STACK CFI records that is obtained when exmining an
+ // executable produced by XCode.
+ SetModuleSymbols(&module1,
+ // Adding a function in CFI.
+ "FUNC 4000 1000 10 enchiridion\n"
+
+ "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: lr\n"
+ "STACK CFI 4001 .cfa: sp 8 + .ra: .cfa -4 + ^"
+ " r7: .cfa -8 + ^\n"
+ "STACK CFI 4002 .cfa: r7 8 +\n"
+ );
+
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x40004010;
+ uint32_t return_address2 = 0x50000900;
+ Label frame1_sp, frame2_sp;
+ Label frame1_fp, frame2_fp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000) // a return address.
+
+ .Mark(&frame1_fp) // Next fp will point to the next value.
+ .D32(frame2_fp) // Save current frame pointer.
+ .D32(return_address2) // Save current link register.
+ .Mark(&frame1_sp)
+
+ // frame 1
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000) // a return address.
+
+ .Mark(&frame2_fp)
+ .D32(0)
+ .D32(0)
+ .Mark(&frame2_sp)
+
+ // frame 2
+ .Append(32, 0) // Whatever values on the stack.
+ .D32(0x0000000D) // junk that's not
+ .D32(0xF0000000); // a return address.
+ RegionFromSection();
+
+
+ raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x50000400;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1;
+ raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value();
+ raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP,
+ &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("module2", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(3U, frames->size());
+
+ StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_LR |
+ StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(frame2_fp.Value(),
+ frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
+ EXPECT_EQ("enchiridion", frame1->function_name);
+ EXPECT_EQ(0x40004000U, frame1->function_base);
+
+
+ StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
+ ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
+ StackFrameARM::CONTEXT_VALID_LR |
+ StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) |
+ StackFrameARM::CONTEXT_VALID_SP),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
+ EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.cc
new file mode 100644
index 000000000..db55d460c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.cc
@@ -0,0 +1,448 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_mips.cc: MIPS-specific stackwalker.
+//
+// See stackwalker_mips.h for documentation.
+//
+// Author: Tata Elxsi
+
+#include "common/scoped_ptr.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/cfi_frame_info.h"
+#include "processor/logging.h"
+#include "processor/postfix_evaluator-inl.h"
+#include "processor/stackwalker_mips.h"
+#include "processor/windows_frame_info.h"
+#include "google_breakpad/common/minidump_cpu_mips.h"
+
+namespace google_breakpad {
+
+StackwalkerMIPS::StackwalkerMIPS(const SystemInfo* system_info,
+ const MDRawContextMIPS* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* resolver_helper)
+: Stackwalker(system_info, memory, modules, resolver_helper),
+ context_(context) {
+ if (context_->context_flags & MD_CONTEXT_MIPS64 ) {
+ if ((memory_ && memory_->GetBase() + memory_->GetSize() - 1)
+ > 0xffffffffffffffff) {
+ BPLOG(ERROR) << "Memory out of range for stackwalking mips64: "
+ << HexString(memory_->GetBase())
+ << "+"
+ << HexString(memory_->GetSize());
+ memory_ = NULL;
+ }
+ } else {
+ if ((memory_ && memory_->GetBase() + memory_->GetSize() - 1) > 0xffffffff) {
+ BPLOG(ERROR) << "Memory out of range for stackwalking mips32: "
+ << HexString(memory_->GetBase())
+ << "+"
+ << HexString(memory_->GetSize());
+ memory_ = NULL;
+ }
+ }
+}
+
+StackFrame* StackwalkerMIPS::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context.";
+ return NULL;
+ }
+
+ StackFrameMIPS* frame = new StackFrameMIPS();
+
+ // 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 = StackFrameMIPS::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.epc;
+
+ return frame;
+}
+
+// Register names for mips.
+static const char* const kRegisterNames[] = {
+ "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$to", "$t1",
+ "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3",
+ "$s4", "$s5", "$s6", "$s7", "$t8", "$t9", "$k0", "$k1", "$gp", "$sp",
+ "$fp", "$ra", NULL
+ // TODO(gordanac): add float point save registers
+};
+
+StackFrameMIPS* StackwalkerMIPS::GetCallerByCFIFrameInfo(
+ const vector<StackFrame*>& frames,
+ CFIFrameInfo* cfi_frame_info) {
+ StackFrameMIPS* last_frame = static_cast<StackFrameMIPS*>(frames.back());
+
+ if (context_->context_flags & MD_CONTEXT_MIPS) {
+ uint32_t sp = 0, pc = 0;
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<uint32_t> callee_registers;
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ caller_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ callee_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ }
+
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &caller_registers)) {
+ return NULL;
+ }
+
+ CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator entry =
+ caller_registers.find(".cfa");
+
+ if (entry != caller_registers.end()) {
+ sp = entry->second;
+ caller_registers["$sp"] = entry->second;
+ }
+
+ entry = caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ caller_registers["$ra"] = entry->second;
+ pc = entry->second - 2 * sizeof(pc);
+ }
+ caller_registers["$pc"] = pc;
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameMIPS> frame(new StackFrameMIPS());
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator caller_entry =
+ caller_registers.find(kRegisterNames[i]);
+
+ if (caller_entry != caller_registers.end()) {
+ // The value of this register is recovered; fill the context with the
+ // value from caller_registers.
+ frame->context.iregs[i] = caller_entry->second;
+ frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+ } else if (((i >= INDEX_MIPS_REG_S0 && i <= INDEX_MIPS_REG_S7) ||
+ (i > INDEX_MIPS_REG_GP && i < INDEX_MIPS_REG_RA)) &&
+ (last_frame->context_validity &
+ StackFrameMIPS::RegisterValidFlag(i))) {
+ // If the STACK CFI data doesn't mention some callee-save register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Calee-save registers according to the MIPS o32 ABI specification are:
+ // $s0 to $s7
+ // $sp, $s8
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+ }
+ }
+
+ frame->context.epc = caller_registers["$pc"];
+ frame->instruction = caller_registers["$pc"];
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_PC;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_RA] = caller_registers["$ra"];
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_RA;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+
+ return frame.release();
+ } else {
+ uint64_t sp = 0, pc = 0;
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<uint64_t> callee_registers;
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<uint64_t> caller_registers;
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ caller_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ callee_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ }
+
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &caller_registers)) {
+ return NULL;
+ }
+
+ CFIFrameInfo::RegisterValueMap<uint64_t>::const_iterator entry =
+ caller_registers.find(".cfa");
+
+ if (entry != caller_registers.end()) {
+ sp = entry->second;
+ caller_registers["$sp"] = entry->second;
+ }
+
+ entry = caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ caller_registers["$ra"] = entry->second;
+ pc = entry->second - 2 * sizeof(pc);
+ }
+ caller_registers["$pc"] = pc;
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameMIPS> frame(new StackFrameMIPS());
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ CFIFrameInfo::RegisterValueMap<uint64_t>::const_iterator caller_entry =
+ caller_registers.find(kRegisterNames[i]);
+
+ if (caller_entry != caller_registers.end()) {
+ // The value of this register is recovered; fill the context with the
+ // value from caller_registers.
+ frame->context.iregs[i] = caller_entry->second;
+ frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+ } else if (((i >= INDEX_MIPS_REG_S0 && i <= INDEX_MIPS_REG_S7) ||
+ (i >= INDEX_MIPS_REG_GP && i < INDEX_MIPS_REG_RA)) &&
+ (last_frame->context_validity &
+ StackFrameMIPS::RegisterValidFlag(i))) {
+ // If the STACK CFI data doesn't mention some callee-save register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Calee-save registers according to the MIPS o32 ABI specification are:
+ // $s0 to $s7
+ // $sp, $s8
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+ }
+ }
+
+ frame->context.epc = caller_registers["$pc"];
+ frame->instruction = caller_registers["$pc"];
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_PC;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_RA] = caller_registers["$ra"];
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_RA;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+
+ return frame.release();
+ }
+}
+
+StackFrame* StackwalkerMIPS::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();
+ StackFrameMIPS* last_frame = static_cast<StackFrameMIPS*>(frames.back());
+ scoped_ptr<StackFrameMIPS> new_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())
+ new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
+
+ // If caller frame is not found in CFI try analyzing the stack.
+ if (stack_scan_allowed && !new_frame.get()) {
+ new_frame.reset(GetCallerByStackScan(frames));
+ }
+
+ // If nothing worked, tell the caller.
+ if (!new_frame.get()) {
+ return NULL;
+ }
+
+ // Treat an instruction address of 0 as end-of-stack.
+ if (new_frame->context.epc == 0) {
+ return NULL;
+ }
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (new_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP] <=
+ last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP]) {
+ return NULL;
+ }
+
+ return new_frame.release();
+}
+
+StackFrameMIPS* StackwalkerMIPS::GetCallerByStackScan(
+ const vector<StackFrame*>& frames) {
+ const uint32_t kMaxFrameStackSize = 1024;
+ const uint32_t kMinArgsOnStack = 4;
+
+ StackFrameMIPS* last_frame = static_cast<StackFrameMIPS*>(frames.back());
+
+ if (context_->context_flags & MD_CONTEXT_MIPS) {
+ uint32_t last_sp = last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP];
+ uint32_t caller_pc, caller_sp, caller_fp;
+
+ // Return address cannot be obtained directly.
+ // Force stackwalking.
+
+ // We cannot use frame pointer to get the return address.
+ // We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols.
+ int count = kMaxFrameStackSize / sizeof(caller_pc);
+
+ if (frames.size() > 1) {
+ // In case of mips32 ABI stack frame of a nonleaf function
+ // must have minimum stack frame assigned for 4 arguments (4 words).
+ // Move stack pointer for 4 words to avoid reporting non-existing frames
+ // for all frames except the topmost one.
+ // There is no way of knowing if topmost frame belongs to a leaf or
+ // a nonleaf function.
+ last_sp += kMinArgsOnStack * sizeof(caller_pc);
+ // Adjust 'count' so that return address is scanned only in limits
+ // of one stack frame.
+ count -= kMinArgsOnStack;
+ }
+
+ do {
+ // Scanning for return address from stack pointer of the last frame.
+ if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, count)) {
+ // If we can't find an instruction pointer even with stack scanning,
+ // give up.
+ BPLOG(ERROR) << " ScanForReturnAddress failed ";
+ return NULL;
+ }
+ // Get $fp stored in the stack frame.
+ if (!memory_->GetMemoryAtAddress(caller_sp - sizeof(caller_pc),
+ &caller_fp)) {
+ BPLOG(INFO) << " GetMemoryAtAddress for fp failed " ;
+ return NULL;
+ }
+
+ count = count - (caller_sp - last_sp) / sizeof(caller_pc);
+ // Now scan the next address in the stack.
+ last_sp = caller_sp + sizeof(caller_pc);
+ } while ((caller_fp - caller_sp >= kMaxFrameStackSize) && count > 0);
+
+ if (!count) {
+ BPLOG(INFO) << " No frame 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 += sizeof(caller_pc);
+ // caller_pc is actually containing $ra value;
+ // $pc is two instructions before $ra,
+ // so the caller_pc needs to be decremented accordingly.
+ caller_pc -= 2 * sizeof(caller_pc);
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameMIPS* frame = new StackFrameMIPS();
+ frame->trust = StackFrame::FRAME_TRUST_SCAN;
+ frame->context = last_frame->context;
+ frame->context.epc = caller_pc;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_PC;
+ frame->instruction = caller_pc;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_SP] = caller_sp;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_SP;
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_FP] = caller_fp;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_FP;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_RA] =
+ caller_pc + 2 * sizeof(caller_pc);
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_RA;
+
+ return frame;
+ } else {
+ uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP];
+ uint64_t caller_pc, caller_sp, caller_fp;
+
+ // Return address cannot be obtained directly.
+ // Force stackwalking.
+
+ // We cannot use frame pointer to get the return address.
+ // We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols.
+ int count = kMaxFrameStackSize / sizeof(caller_pc);
+
+ do {
+ // Scanning for return address from stack pointer of the last frame.
+ if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, count)) {
+ // If we can't find an instruction pointer even with stack scanning,
+ // give up.
+ BPLOG(ERROR) << " ScanForReturnAddress failed ";
+ return NULL;
+ }
+ // Get $fp stored in the stack frame.
+ if (!memory_->GetMemoryAtAddress(caller_sp - sizeof(caller_pc),
+ &caller_fp)) {
+ BPLOG(INFO) << " GetMemoryAtAddress for fp failed " ;
+ return NULL;
+ }
+
+ count = count - (caller_sp - last_sp) / sizeof(caller_pc);
+ // Now scan the next address in the stack.
+ last_sp = caller_sp + sizeof(caller_pc);
+ } while ((caller_fp - caller_sp >= kMaxFrameStackSize) && count > 0);
+
+ if (!count) {
+ BPLOG(INFO) << " No frame 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 += sizeof(caller_pc);
+ // caller_pc is actually containing $ra value;
+ // $pc is two instructions before $ra,
+ // so the caller_pc needs to be decremented accordingly.
+ caller_pc -= 2 * sizeof(caller_pc);
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameMIPS* frame = new StackFrameMIPS();
+ frame->trust = StackFrame::FRAME_TRUST_SCAN;
+ frame->context = last_frame->context;
+ frame->context.epc = caller_pc;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_PC;
+ frame->instruction = caller_pc;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_SP] = caller_sp;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_SP;
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_FP] = caller_fp;
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_FP;
+
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_RA] =
+ caller_pc + 2 * sizeof(caller_pc);
+ frame->context_validity |= StackFrameMIPS::CONTEXT_VALID_RA;
+
+ return frame;
+ }
+}
+
+} // namespace google_breakpad
+
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.h
new file mode 100644
index 000000000..5f97791fb
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_mips.h: MIPS-specific stackwalker.
+//
+// Provides stack frames given MIPS register context and a memory region
+// corresponding to a MIPSstack.
+//
+// Author: Tata Elxsi
+
+#ifndef PROCESSOR_STACKWALKER_MIPS_H__
+#define PROCESSOR_STACKWALKER_MIPS_H__
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stackwalker.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/cfi_frame_info.h"
+
+namespace google_breakpad {
+
+class CodeModules;
+
+class StackwalkerMIPS : public Stackwalker {
+ public:
+ // Context is a MIPS context object that gives access to mips-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.
+ StackwalkerMIPS(const SystemInfo* system_info,
+ const MDRawContextMIPS* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // Implementation of Stackwalker, using mips 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.
+ StackFrameMIPS* GetCallerByCFIFrameInfo(const vector<StackFrame*>& frames,
+ CFIFrameInfo* cfi_frame_info);
+
+ // Scan the stack for plausible return address and frame pointer pair.
+ // The caller takes ownership of the returned frame. Return NULL on failure.
+ StackFrameMIPS* GetCallerByStackScan(const vector<StackFrame*>& frames);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextMIPS* context_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STACKWALKER_MIPS_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips_unittest.cc
new file mode 100644
index 000000000..5398c2b33
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_mips_unittest.cc
@@ -0,0 +1,707 @@
+// Copyright (c) 2013, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Gordana Cmiljanovic <gordana.cmiljanovic@imgtec.com>
+
+// stackwalker_mips_unittest.cc: Unit tests for StackwalkerMIPS class.
+
+#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_mips.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameMIPS;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerMIPS;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerMIPSFixture {
+ public:
+ StackwalkerMIPSFixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x00400000, 0x10000, "module1", "version1"),
+ module2(0x00500000, 0x10000, "module2", "version2") {
+ // Identify the system as a Linux system.
+ system_info.os = "Linux";
+ system_info.os_short = "linux";
+ system_info.os_version = "Observant Opossum"; // Jealous Jellyfish
+ system_info.cpu = "mips";
+ 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(MDRawContextMIPS* 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;
+ MDRawContextMIPS raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame*>* frames;
+};
+
+class SanityCheck: public StackwalkerMIPSFixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0x0);
+ RegionFromSection();
+ raw_context.epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerMIPS 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_EQ(1U, frames->size());
+ StackFrameMIPS* frame = static_cast<StackFrameMIPS*>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetContextFrame: public StackwalkerMIPSFixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0x0);
+ RegionFromSection();
+ raw_context.epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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();
+ StackFrameMIPS* frame = static_cast<StackFrameMIPS*>(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.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ raw_context.epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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();
+ StackFrameMIPS* frame = static_cast<StackFrameMIPS*>(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 StackwalkerMIPSFixture, public Test { };
+
+TEST_F(GetCallerFrame, ScanWithoutSymbols) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ // When the stack walker resorts to scanning the stack,
+ // only addresses located within loaded modules are
+ // considered valid return addresses.
+ // Force scanning through three frames to ensure that the
+ // stack pointer is set properly in scan-recovered frames.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x00400100;
+ uint32_t return_address2 = 0x00400900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D32(0x00490000) // junk that's not
+ .D32(0x00600000) // a return address
+
+ .D32(frame1_sp) // stack pointer
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(16, 0) // space
+
+ .D32(0xF0000000) // more junk
+ .D32(0x0000000D)
+
+ .D32(frame2_sp) // stack pointer
+ .D32(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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_EQ(3U, frames->size());
+
+ StackFrameMIPS* frame0 = static_cast<StackFrameMIPS*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameMIPS::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameMIPS* frame1 = static_cast<StackFrameMIPS*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameMIPS::CONTEXT_VALID_PC |
+ StackFrameMIPS::CONTEXT_VALID_SP |
+ StackFrameMIPS::CONTEXT_VALID_FP |
+ StackFrameMIPS::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1 - 2 * sizeof(return_address1), frame1->context.epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_MIPS_REG_SP]);
+
+ StackFrameMIPS* frame2 = static_cast<StackFrameMIPS*>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
+ ASSERT_EQ((StackFrameMIPS::CONTEXT_VALID_PC |
+ StackFrameMIPS::CONTEXT_VALID_SP |
+ StackFrameMIPS::CONTEXT_VALID_FP |
+ StackFrameMIPS::CONTEXT_VALID_RA),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2 - 2 * sizeof(return_address2), frame2->context.epc);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_MIPS_REG_SP]);
+}
+
+TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ // During stack scanning, if a potential return address
+ // is located within a loaded module that has symbols,
+ // it is only considered a valid return address if it
+ // lies within a function's bounds.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address = 0x00500200;
+ Label frame1_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D32(0x00490000) // junk that's not
+ .D32(0x00600000) // a return address
+
+ .D32(0x00401000) // a couple of plausible addresses
+ .D32(0x0050F000) // that are not within functions
+
+ .D32(frame1_sp) // stack pointer
+ .D32(return_address) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.epc = 0x00400200;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = return_address;
+
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 100 400 10 monotreme\n");
+ SetModuleSymbols(&module2,
+ // The calling frame's function.
+ "FUNC 100 400 10 marsupial\n");
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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());
+
+ StackFrameMIPS* frame0 = static_cast<StackFrameMIPS*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameMIPS::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+ EXPECT_EQ("monotreme", frame0->function_name);
+ EXPECT_EQ(0x00400100U, frame0->function_base);
+
+ StackFrameMIPS* frame1 = static_cast<StackFrameMIPS*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameMIPS::CONTEXT_VALID_PC |
+ StackFrameMIPS::CONTEXT_VALID_SP |
+ StackFrameMIPS::CONTEXT_VALID_FP |
+ StackFrameMIPS::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address - 2 * sizeof(return_address), frame1->context.epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_MIPS_REG_SP]);
+ EXPECT_EQ("marsupial", frame1->function_name);
+ EXPECT_EQ(0x00500100U, frame1->function_base);
+}
+
+TEST_F(GetCallerFrame, CheckStackFrameSizeLimit) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ // If the stackwalker resorts to stack scanning, it will scan only
+ // 1024 bytes of stack which correspondes to maximum size of stack frame.
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x00500100;
+ uint32_t return_address2 = 0x00500900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D32(0x00490000) // junk that's not
+ .D32(0x00600000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D32(frame1_sp) // stack pointer
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(128 * 4, 0) // space
+
+ .D32(0x00F00000) // more junk
+ .D32(0x0000000D)
+
+ .Append(128 * 4, 0) // more space
+
+ .D32(frame2_sp) // stack pointer
+ .D32(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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(2U, frames->size());
+
+ StackFrameMIPS* frame0 = static_cast<StackFrameMIPS*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameMIPS::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameMIPS* frame1 = static_cast<StackFrameMIPS*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameMIPS::CONTEXT_VALID_PC |
+ StackFrameMIPS::CONTEXT_VALID_SP |
+ StackFrameMIPS::CONTEXT_VALID_FP |
+ StackFrameMIPS::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1 - 2 * sizeof(return_address1), frame1->context.epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_MIPS_REG_SP]);
+}
+
+// Test that set_max_frames_scanned prevents using stack scanning
+// to find caller frames.
+TEST_F(GetCallerFrame, ScanningNotAllowed) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ // When the stack walker resorts to scanning the stack,
+ // only fixed number of frames are allowed to be scanned out from stack
+ stack_section.start() = 0x80000000;
+ uint32_t return_address1 = 0x00500100;
+ uint32_t return_address2 = 0x00500900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D32(0x00490000) // junk that's not
+ .D32(0x00600000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D32(frame1_sp) // stack pointer
+ .D32(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(128 * 4, 0) // space
+
+ .D32(0x00F00000) // more junk
+ .D32(0x0000000D)
+
+ .Append(128 * 4, 0) // more space
+
+ .D32(frame2_sp) // stack pointer
+ .D32(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(32, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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());
+
+ StackFrameMIPS* frame0 = static_cast<StackFrameMIPS*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameMIPS::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+}
+
+struct CFIFixture: public StackwalkerMIPSFixture {
+ CFIFixture() {
+ // Provide some STACK CFI records;
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 0 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the $ra register.
+ "STACK CFI INIT 4000 1000 .cfa: $sp 0 + .ra: $ra\n"
+ // Move stack pointer.
+ "STACK CFI 4004 .cfa: $sp 32 +\n"
+ // store $fp and ra
+ "STACK CFI 4008 $fp: .cfa -8 + ^ .ra: .cfa -4 + ^\n"
+ // restore $fp
+ "STACK CFI 400c .cfa: $fp 32 +\n"
+ // restore $sp
+ "STACK CFI 4018 .cfa: $sp 32 +\n"
+
+ "STACK CFI 4020 $fp: $fp .cfa: $sp 0 + .ra: .ra\n"
+
+ // The calling function.
+ "FUNC 5000 1000 0 epictetus\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the $ra register.
+ "STACK CFI INIT 5000 1000 .cfa: $sp .ra: $ra\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 8 .cfa: $sp 0 + .ra: $ra\n"
+
+ // A function whose CFI makes the stack pointer
+ // go backwards.
+ "FUNC 6000 1000 20 palinal\n"
+ "STACK CFI INIT 6000 1000 .cfa: $sp 4 - .ra: $ra\n"
+
+ // A function with CFI expressions that can't be
+ // evaluated.
+ "FUNC 7000 1000 20 rhetorical\n"
+ "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n"
+ );
+
+ // Provide some distinctive values for the caller's registers.
+ expected.epc = 0x00405508;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S0] = 0x0;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S1] = 0x1;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S2] = 0x2;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S3] = 0x3;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S4] = 0x4;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S5] = 0x5;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S6] = 0x6;
+ expected.iregs[MD_CONTEXT_MIPS_REG_S7] = 0x7;
+ expected.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+ expected.iregs[MD_CONTEXT_MIPS_REG_FP] = 0x80000000;
+ expected.iregs[MD_CONTEXT_MIPS_REG_RA] = 0x00405510;
+
+ // Expect CFI to recover all callee-save registers. Since CFI is the
+ // only stack frame construction technique we have, aside from the
+ // context frame itself, there's no way for us to have a set of valid
+ // registers smaller than this.
+ expected_validity = (StackFrameMIPS::CONTEXT_VALID_PC |
+ StackFrameMIPS::CONTEXT_VALID_S0 |
+ StackFrameMIPS::CONTEXT_VALID_S1 |
+ StackFrameMIPS::CONTEXT_VALID_S2 |
+ StackFrameMIPS::CONTEXT_VALID_S3 |
+ StackFrameMIPS::CONTEXT_VALID_S4 |
+ StackFrameMIPS::CONTEXT_VALID_S5 |
+ StackFrameMIPS::CONTEXT_VALID_S6 |
+ StackFrameMIPS::CONTEXT_VALID_S7 |
+ StackFrameMIPS::CONTEXT_VALID_SP |
+ StackFrameMIPS::CONTEXT_VALID_FP |
+ StackFrameMIPS::CONTEXT_VALID_RA);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameMIPS::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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());
+
+ StackFrameMIPS* frame0 = static_cast<StackFrameMIPS*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameMIPS::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x00404000U, frame0->function_base);
+
+ StackFrameMIPS* frame1 = static_cast<StackFrameMIPS*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S0],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S0]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S1],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S1]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S2],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S2]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S3],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S3]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S4],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S4]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S5],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S5]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S6],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S6]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_S7],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_S7]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_FP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_RA],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_RA]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_MIPS_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_MIPS_REG_SP]);
+ EXPECT_EQ(expected.epc, frame1->context.epc);
+ EXPECT_EQ(expected.epc, frame1->instruction);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ EXPECT_EQ(0x00405000U, frame1->function_base);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextMIPS expected;
+
+ // The validity mask for expected.
+ int expected_validity;
+
+ // The validity mask to impose on the context frame.
+ int context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+// TODO(gordanac): add CFI tests
+
+TEST_F(CFI, At4004) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ Label frame1_sp = expected.iregs[MD_CONTEXT_MIPS_REG_SP];
+ stack_section
+ // frame0
+ .Append(24, 0) // space
+ .D32(frame1_sp) // stack pointer
+ .D32(0x00405510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.epc = 0x00404004;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ raw_context.epc = 0x40005000;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = 0x00405510;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.context_flags = raw_context.context_flags | MD_CONTEXT_MIPS_FULL;
+ raw_context.epc = 0x00407000;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_MIPS_REG_RA] = 0x00405510;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerMIPS 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(1U, frames->size());
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc
new file mode 100644
index 000000000..7e2088440
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_ppc.cc: ppc-specific stackwalker.
+//
+// See stackwalker_ppc.h for documentation.
+//
+// Author: Mark Mentovai
+
+
+#include "processor/stackwalker_ppc.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+
+StackwalkerPPC::StackwalkerPPC(const SystemInfo* system_info,
+ const MDRawContextPPC* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* resolver_helper)
+ : Stackwalker(system_info, memory, modules, resolver_helper),
+ context_(context) {
+ if (memory_ && memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ // This implementation only covers 32-bit ppc CPUs. The limits of the
+ // supplied stack are invalid. Mark memory_ = NULL, which will cause
+ // stackwalking to fail.
+ BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
+ HexString(memory_->GetBase()) << "+" <<
+ HexString(memory_->GetSize());
+ memory_ = NULL;
+ }
+}
+
+
+StackFrame* StackwalkerPPC::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
+ return NULL;
+ }
+
+ StackFramePPC* frame = new StackFramePPC();
+
+ // The instruction pointer is stored directly in a register, so pull it
+ // straight out of the CPU context structure.
+ frame->context = *context_;
+ frame->context_validity = StackFramePPC::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.srr0;
+
+ return frame;
+}
+
+
+StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ // The instruction pointers for previous frames are saved on the stack.
+ // The typical ppc calling convention is for the called procedure to store
+ // its return address in the calling procedure's stack frame at 8(%r1),
+ // and to allocate its own stack frame by decrementing %r1 (the stack
+ // pointer) and saving the old value of %r1 at 0(%r1). Because the ppc has
+ // no hardware stack, there is no distinction between the stack pointer and
+ // frame pointer, and what is typically thought of as the frame pointer on
+ // an x86 is usually referred to as the stack pointer on a ppc.
+
+ StackFramePPC* last_frame = static_cast<StackFramePPC*>(
+ stack->frames()->back());
+
+ // A caller frame must reside higher in memory than its callee frames.
+ // Anything else is an error, or an indication that we've reached the
+ // end of the stack.
+ uint32_t stack_pointer;
+ if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1],
+ &stack_pointer) ||
+ stack_pointer <= last_frame->context.gpr[1]) {
+ return NULL;
+ }
+
+ // Mac OS X/Darwin gives 1 as the return address from the bottom-most
+ // frame in a stack (a thread's entry point). I haven't found any
+ // documentation on this, but 0 or 1 would be bogus return addresses,
+ // so check for them here and return false (end of stack) when they're
+ // hit to avoid having a phantom frame.
+ uint32_t instruction;
+ if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction) ||
+ instruction <= 1) {
+ return NULL;
+ }
+
+ StackFramePPC* frame = new StackFramePPC();
+
+ frame->context = last_frame->context;
+ frame->context.srr0 = instruction;
+ frame->context.gpr[1] = stack_pointer;
+ frame->context_validity = StackFramePPC::CONTEXT_VALID_SRR0 |
+ StackFramePPC::CONTEXT_VALID_GPR1;
+ frame->trust = StackFrame::FRAME_TRUST_FP;
+
+ // frame->context.srr0 is the return address, which is one instruction
+ // past the branch that caused us to arrive at the callee. Set
+ // frame_ppc->instruction to four less than that. Since all ppc
+ // instructions are 4 bytes wide, this is the address of the branch
+ // instruction. This allows source line information to match up with the
+ // line that contains a function call. Callers that require the exact
+ // return address value may access the context.srr0 field of StackFramePPC.
+ frame->instruction = frame->context.srr0 - 4;
+
+ return frame;
+}
+
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h
new file mode 100644
index 000000000..012e5c32f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_ppc.h: ppc-specific stackwalker.
+//
+// Provides stack frames given ppc register context and a memory region
+// corresponding to a ppc stack.
+//
+// Author: Mark Mentovai
+
+
+#ifndef PROCESSOR_STACKWALKER_PPC_H__
+#define PROCESSOR_STACKWALKER_PPC_H__
+
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stackwalker.h"
+
+namespace google_breakpad {
+
+class CodeModules;
+
+class StackwalkerPPC : public Stackwalker {
+ public:
+ // context is a ppc context object that gives access to ppc-specific
+ // register state corresponding to the innermost called frame to be
+ // included in the stack. The other arguments are passed directly through
+ // to the base Stackwalker constructor.
+ StackwalkerPPC(const SystemInfo* system_info,
+ const MDRawContextPPC* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // Implementation of Stackwalker, using ppc context (stack pointer in %r1,
+ // saved program counter in %srr0) and stack conventions (saved stack
+ // pointer at 0(%r1), return address at 8(0(%r1)).
+ virtual StackFrame* GetContextFrame();
+ virtual StackFrame* GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextPPC* context_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STACKWALKER_PPC_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.cc
new file mode 100644
index 000000000..51c71fe56
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_ppc64.cc: ppc64-specific stackwalker.
+//
+// See stackwalker_ppc64.h for documentation.
+
+
+#include "processor/stackwalker_ppc64.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+
+#include <stdio.h>
+
+namespace google_breakpad {
+
+
+StackwalkerPPC64::StackwalkerPPC64(const SystemInfo* system_info,
+ const MDRawContextPPC64* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* resolver_helper)
+ : Stackwalker(system_info, memory, modules, resolver_helper),
+ context_(context) {
+}
+
+
+StackFrame* StackwalkerPPC64::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
+ return NULL;
+ }
+
+ StackFramePPC64* frame = new StackFramePPC64();
+
+ // 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 = StackFramePPC64::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.srr0;
+
+ return frame;
+}
+
+
+StackFrame* StackwalkerPPC64::GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ // The instruction pointers for previous frames are saved on the stack.
+ // The typical ppc64 calling convention is for the called procedure to store
+ // its return address in the calling procedure's stack frame at 8(%r1),
+ // and to allocate its own stack frame by decrementing %r1 (the stack
+ // pointer) and saving the old value of %r1 at 0(%r1). Because the ppc64 has
+ // no hardware stack, there is no distinction between the stack pointer and
+ // frame pointer, and what is typically thought of as the frame pointer on
+ // an x86 is usually referred to as the stack pointer on a ppc64.
+
+ StackFramePPC64* last_frame = static_cast<StackFramePPC64*>(
+ stack->frames()->back());
+
+ // A caller frame must reside higher in memory than its callee frames.
+ // Anything else is an error, or an indication that we've reached the
+ // end of the stack.
+ uint64_t stack_pointer;
+ if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1],
+ &stack_pointer) ||
+ stack_pointer <= last_frame->context.gpr[1]) {
+ return NULL;
+ }
+
+ // Mac OS X/Darwin gives 1 as the return address from the bottom-most
+ // frame in a stack (a thread's entry point). I haven't found any
+ // documentation on this, but 0 or 1 would be bogus return addresses,
+ // so check for them here and return false (end of stack) when they're
+ // hit to avoid having a phantom frame.
+ uint64_t instruction;
+ if (!memory_->GetMemoryAtAddress(stack_pointer + 16, &instruction) ||
+ instruction <= 1) {
+ return NULL;
+ }
+
+ StackFramePPC64* frame = new StackFramePPC64();
+
+ frame->context = last_frame->context;
+ frame->context.srr0 = instruction;
+ frame->context.gpr[1] = stack_pointer;
+ frame->context_validity = StackFramePPC64::CONTEXT_VALID_SRR0 |
+ StackFramePPC64::CONTEXT_VALID_GPR1;
+ frame->trust = StackFrame::FRAME_TRUST_FP;
+
+ // frame->context.srr0 is the return address, which is one instruction
+ // past the branch that caused us to arrive at the callee. Set
+ // frame_ppc64->instruction to eight less than that. Since all ppc64
+ // instructions are 8 bytes wide, this is the address of the branch
+ // instruction. This allows source line information to match up with the
+ // line that contains a function call. Callers that require the exact
+ // return address value may access the context.srr0 field of StackFramePPC64.
+ frame->instruction = frame->context.srr0 - 8;
+
+ return frame;
+}
+
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.h
new file mode 100644
index 000000000..a406343af
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc64.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_ppc64.h: ppc-specific stackwalker.
+//
+// Provides stack frames given ppc64 register context and a memory region
+// corresponding to a ppc64 stack.
+
+
+#ifndef PROCESSOR_STACKWALKER_PPC64_H__
+#define PROCESSOR_STACKWALKER_PPC64_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 StackwalkerPPC64 : public Stackwalker {
+ public:
+ // context is a ppc64 context object that gives access to ppc64-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.
+ StackwalkerPPC64(const SystemInfo* system_info,
+ const MDRawContextPPC64* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // Implementation of Stackwalker, using ppc64 context (stack pointer in %r1,
+ // saved program counter in %srr0) and stack conventions (saved stack
+ // pointer at 0(%r1), return address at 8(0(%r1)).
+ virtual StackFrame* GetContextFrame();
+ virtual StackFrame* GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextPPC64* context_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STACKWALKER_PPC64_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest.cc
new file mode 100644
index 000000000..f692d4c4c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest.cc
@@ -0,0 +1,433 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the
+// running process' stack as test data, if running on an x86 or ppc and
+// compiled with gcc. This test is not enabled in the "make check" suite
+// by default, because certain optimizations interfere with its proper
+// operation. To turn it on, configure with --enable-selftest.
+//
+// Optimizations that cause problems:
+// - stack frame reuse. The Recursor function here calls itself with
+// |return Recursor|. When the caller's frame is reused, it will cause
+// CountCallerFrames to correctly return the same number of frames
+// in both the caller and callee. This is considered an unexpected
+// condition in the test, which expects a callee to have one more
+// caller frame in the stack than its caller.
+// - frame pointer omission. Even with a stackwalker that understands
+// this optimization, the code to harness debug information currently
+// only exists to retrieve it from minidumps, not the current process.
+//
+// This test can also serve as a developmental and debugging aid if
+// PRINT_STACKS is defined.
+//
+// Author: Mark Mentovai
+
+#include <assert.h>
+
+#include "processor/logging.h"
+
+#if defined(__i386) && !defined(__i386__)
+#define __i386__
+#endif
+#if defined(__sparc) && !defined(__sparc__)
+#define __sparc__
+#endif
+
+#if (defined(__SUNPRO_CC) || defined(__GNUC__)) && \
+ (defined(__i386__) || defined(__ppc__) || defined(__sparc__))
+
+
+#include <stdio.h>
+
+#include "common/scoped_ptr.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::MemoryRegion;
+using google_breakpad::scoped_ptr;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFramePPC;
+using google_breakpad::StackFrameX86;
+using google_breakpad::StackFrameSPARC;
+
+#if defined(__i386__)
+#include "processor/stackwalker_x86.h"
+using google_breakpad::StackwalkerX86;
+#elif defined(__ppc__)
+#include "processor/stackwalker_ppc.h"
+using google_breakpad::StackwalkerPPC;
+#elif defined(__sparc__)
+#include "processor/stackwalker_sparc.h"
+using google_breakpad::StackwalkerSPARC;
+#endif // __i386__ || __ppc__ || __sparc__
+
+#define RECURSION_DEPTH 100
+
+
+// A simple MemoryRegion subclass that provides direct access to this
+// process' memory space by pointer.
+class SelfMemoryRegion : public MemoryRegion {
+ public:
+ virtual uint64_t GetBase() const { return 0; }
+ virtual uint32_t GetSize() const { return 0xffffffff; }
+
+ bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const {
+ return GetMemoryAtAddressInternal(address, value); }
+ bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const {
+ return GetMemoryAtAddressInternal(address, value); }
+ bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const {
+ return GetMemoryAtAddressInternal(address, value); }
+ bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const {
+ return GetMemoryAtAddressInternal(address, value); }
+ void Print() const {
+ assert(false);
+ }
+
+ private:
+ template<typename T> bool GetMemoryAtAddressInternal(uint64_t address,
+ T* value) {
+ // Without knowing what addresses are actually mapped, just assume that
+ // everything low is not mapped. This helps the stackwalker catch the
+ // end of a stack when it tries to dereference a null or low pointer
+ // in an attempt to find the caller frame. Other unmapped accesses will
+ // cause the program to crash, but that would properly be a test failure.
+ if (address < 0x100)
+ return false;
+
+ uint8_t* memory = 0;
+ *value = *reinterpret_cast<const T*>(&memory[address]);
+ return true;
+ }
+};
+
+
+#if defined(__GNUC__)
+
+
+#if defined(__i386__)
+
+// GetEBP returns the current value of the %ebp register. Because it's
+// implemented as a function, %ebp itself contains GetEBP's frame pointer
+// and not the caller's frame pointer. Dereference %ebp to obtain the
+// caller's frame pointer, which the compiler-generated preamble stored
+// on the stack (provided frame pointers are not being omitted.) Because
+// this function depends on the compiler-generated preamble, inlining is
+// disabled.
+static uint32_t GetEBP() __attribute__((noinline));
+static uint32_t GetEBP() {
+ uint32_t ebp;
+ __asm__ __volatile__(
+ "movl (%%ebp), %0"
+ : "=a" (ebp)
+ );
+ return ebp;
+}
+
+
+// The caller's %esp is 8 higher than the value of %ebp in this function,
+// assuming that it's not inlined and that the standard prolog is used.
+// The CALL instruction places a 4-byte return address on the stack above
+// the caller's %esp, and this function's prolog will save the caller's %ebp
+// on the stack as well, for another 4 bytes, before storing %esp in %ebp.
+static uint32_t GetESP() __attribute__((noinline));
+static uint32_t GetESP() {
+ uint32_t ebp;
+ __asm__ __volatile__(
+ "movl %%ebp, %0"
+ : "=a" (ebp)
+ );
+ return ebp + 8;
+}
+
+
+// GetEIP returns the instruction pointer identifying the next instruction
+// to execute after GetEIP returns. It obtains this information from the
+// stack, where it was placed by the call instruction that called GetEIP.
+// This function depends on frame pointers not being omitted. It is possible
+// to write a pure asm version of this routine that has no compiler-generated
+// preamble and uses %esp instead of %ebp; that would function in the
+// absence of frame pointers. However, the simpler approach is used here
+// because GetEBP and stackwalking necessarily depends on access to frame
+// pointers. Because this function depends on a call instruction and the
+// compiler-generated preamble, inlining is disabled.
+static uint32_t GetEIP() __attribute__((noinline));
+static uint32_t GetEIP() {
+ uint32_t eip;
+ __asm__ __volatile__(
+ "movl 4(%%ebp), %0"
+ : "=a" (eip)
+ );
+ return eip;
+}
+
+
+#elif defined(__ppc__)
+
+
+// GetSP returns the current value of the %r1 register, which by convention,
+// is the stack pointer on ppc. Because it's implemented as a function,
+// %r1 itself contains GetSP's own stack pointer and not the caller's stack
+// pointer. Dereference %r1 to obtain the caller's stack pointer, which the
+// compiler-generated prolog stored on the stack. Because this function
+// depends on the compiler-generated prolog, inlining is disabled.
+static uint32_t GetSP() __attribute__((noinline));
+static uint32_t GetSP() {
+ uint32_t sp;
+ __asm__ __volatile__(
+ "lwz %0, 0(r1)"
+ : "=r" (sp)
+ );
+ return sp;
+}
+
+
+// GetPC returns the program counter identifying the next instruction to
+// execute after GetPC returns. It obtains this information from the
+// link register, where it was placed by the branch instruction that called
+// GetPC. Because this function depends on the caller's use of a branch
+// instruction, inlining is disabled.
+static uint32_t GetPC() __attribute__((noinline));
+static uint32_t GetPC() {
+ uint32_t lr;
+ __asm__ __volatile__(
+ "mflr %0"
+ : "=r" (lr)
+ );
+ return lr;
+}
+
+
+#elif defined(__sparc__)
+
+
+// GetSP returns the current value of the %sp/%o6/%g_r[14] register, which
+// by convention, is the stack pointer on sparc. Because it's implemented
+// as a function, %sp itself contains GetSP's own stack pointer and not
+// the caller's stack pointer. Dereference to obtain the caller's stack
+// pointer, which the compiler-generated prolog stored on the stack.
+// Because this function depends on the compiler-generated prolog, inlining
+// is disabled.
+static uint32_t GetSP() __attribute__((noinline));
+static uint32_t GetSP() {
+ uint32_t sp;
+ __asm__ __volatile__(
+ "mov %%fp, %0"
+ : "=r" (sp)
+ );
+ return sp;
+}
+
+// GetFP returns the current value of the %fp register. Because it's
+// implemented as a function, %fp itself contains GetFP's frame pointer
+// and not the caller's frame pointer. Dereference %fp to obtain the
+// caller's frame pointer, which the compiler-generated preamble stored
+// on the stack (provided frame pointers are not being omitted.) Because
+// this function depends on the compiler-generated preamble, inlining is
+// disabled.
+static uint32_t GetFP() __attribute__((noinline));
+static uint32_t GetFP() {
+ uint32_t fp;
+ __asm__ __volatile__(
+ "ld [%%fp+56], %0"
+ : "=r" (fp)
+ );
+ return fp;
+}
+
+// GetPC returns the program counter identifying the next instruction to
+// execute after GetPC returns. It obtains this information from the
+// link register, where it was placed by the branch instruction that called
+// GetPC. Because this function depends on the caller's use of a branch
+// instruction, inlining is disabled.
+static uint32_t GetPC() __attribute__((noinline));
+static uint32_t GetPC() {
+ uint32_t pc;
+ __asm__ __volatile__(
+ "mov %%i7, %0"
+ : "=r" (pc)
+ );
+ return pc + 8;
+}
+
+#endif // __i386__ || __ppc__ || __sparc__
+
+#elif defined(__SUNPRO_CC)
+
+#if defined(__i386__)
+extern "C" {
+extern uint32_t GetEIP();
+extern uint32_t GetEBP();
+extern uint32_t GetESP();
+}
+#elif defined(__sparc__)
+extern "C" {
+extern uint32_t GetPC();
+extern uint32_t GetFP();
+extern uint32_t GetSP();
+}
+#endif // __i386__ || __sparc__
+
+#endif // __GNUC__ || __SUNPRO_CC
+
+// CountCallerFrames returns the number of stack frames beneath the function
+// that called CountCallerFrames. Because this function's return value
+// is dependent on the size of the stack beneath it, inlining is disabled,
+// and any function that calls this should not be inlined either.
+#if defined(__GNUC__)
+static unsigned int CountCallerFrames() __attribute__((noinline));
+#elif defined(__SUNPRO_CC)
+static unsigned int CountCallerFrames();
+#endif
+static unsigned int CountCallerFrames() {
+ SelfMemoryRegion memory;
+ BasicSourceLineResolver resolver;
+
+#if defined(__i386__)
+ MDRawContextX86 context = MDRawContextX86();
+ context.eip = GetEIP();
+ context.ebp = GetEBP();
+ context.esp = GetESP();
+
+ StackwalkerX86 stackwalker = StackwalkerX86(NULL, &context, &memory, NULL,
+ NULL, &resolver);
+#elif defined(__ppc__)
+ MDRawContextPPC context = MDRawContextPPC();
+ context.srr0 = GetPC();
+ context.gpr[1] = GetSP();
+
+ StackwalkerPPC stackwalker = StackwalkerPPC(NULL, &context, &memory, NULL,
+ NULL, &resolver);
+#elif defined(__sparc__)
+ MDRawContextSPARC context = MDRawContextSPARC();
+ context.pc = GetPC();
+ context.g_r[14] = GetSP();
+ context.g_r[30] = GetFP();
+
+ StackwalkerSPARC stackwalker = StackwalkerSPARC(NULL, &context, &memory,
+ NULL, NULL, &resolver);
+#endif // __i386__ || __ppc__ || __sparc__
+
+ CallStack stack;
+ vector<const CodeModule*> modules_without_symbols;
+ stackwalker.Walk(&stack, &modules_without_symbols);
+
+#ifdef PRINT_STACKS
+ printf("\n");
+ for (unsigned int frame_index = 0;
+ frame_index < stack.frames()->size();
+ ++frame_index) {
+ StackFrame *frame = stack.frames()->at(frame_index);
+ printf("frame %-3d instruction = 0x%08" PRIx64,
+ frame_index, frame->instruction);
+#if defined(__i386__)
+ StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame);
+ printf(" esp = 0x%08x ebp = 0x%08x\n",
+ frame_x86->context.esp, frame_x86->context.ebp);
+#elif defined(__ppc__)
+ StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame);
+ printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]);
+#elif defined(__sparc__)
+ StackFrameSPARC *frame_sparc = reinterpret_cast<StackFrameSPARC*>(frame);
+ printf(" sp = 0x%08x fp = 0x%08x\n",
+ frame_sparc->context.g_r[14], frame_sparc->context.g_r[30]);
+#endif // __i386__ || __ppc__ || __sparc__
+ }
+#endif // PRINT_STACKS
+
+ // Subtract 1 because the caller wants the number of frames beneath
+ // itself. Because the caller called us, subract two for our frame and its
+ // frame, which are included in stack.size().
+ return stack.frames()->size() - 2;
+}
+
+
+// Recursor verifies that the number stack frames beneath itself is one more
+// than the number of stack frames beneath its parent. When depth frames
+// have been reached, Recursor stops checking and returns success. If the
+// frame count check fails at any depth, Recursor will stop and return false.
+// Because this calls CountCallerFrames, inlining is disabled.
+#if defined(__GNUC__)
+static bool Recursor(unsigned int depth, unsigned int parent_callers)
+ __attribute__((noinline));
+#elif defined(__SUNPRO_CC)
+static bool Recursor(unsigned int depth, unsigned int parent_callers);
+#endif
+static bool Recursor(unsigned int depth, unsigned int parent_callers) {
+ unsigned int callers = CountCallerFrames();
+ if (callers != parent_callers + 1)
+ return false;
+
+ if (depth)
+ return Recursor(depth - 1, callers);
+
+ // depth == 0
+ return true;
+}
+
+
+// Because this calls CountCallerFrames, inlining is disabled - but because
+// it's main (and nobody calls it other than the entry point), it wouldn't
+// be inlined anyway.
+#if defined(__GNUC__)
+int main(int argc, char** argv) __attribute__((noinline));
+#elif defined(__SUNPRO_CC)
+int main(int argc, char** argv);
+#endif
+int main(int argc, char** argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1;
+}
+
+
+#else
+// Not i386 or ppc or sparc? We can only test stacks we know how to walk.
+
+
+int main(int argc, char **argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ // "make check" interprets an exit status of 77 to mean that the test is
+ // not supported.
+ BPLOG(ERROR) << "Selftest not supported here";
+ return 77;
+}
+
+
+#endif // (__GNUC__ || __SUNPRO_CC) && (__i386__ || __ppc__ || __sparc__)
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest_sol.s b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest_sol.s
new file mode 100644
index 000000000..648b0499a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_selftest_sol.s
@@ -0,0 +1,111 @@
+/* Copyright (c) 2007, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* stackwalker_selftest_sol.s
+ * On Solaris, the recommeded compiler is CC, so we can not use gcc inline
+ * asm, use this method instead.
+ *
+ * How to compile: as -P -L -D_ASM -D_STDC -K PIC -o \
+ * src/processor/stackwalker_selftest_sol.o \
+ * src/processor/stackwalker_selftest_sol.s
+ *
+ * Author: Michael Shang
+ */
+
+#include <sys/asm_linkage.h>
+
+#if defined(__i386)
+
+
+ENTRY(GetEBP)
+ pushl %ebp
+ movl %esp,%ebp
+ subl $0x00000004,%esp
+ movl 0x00000000(%ebp),%eax
+ movl %eax,0xfffffffc(%ebp)
+ movl 0xfffffffc(%ebp),%eax
+ leave
+ ret
+SET_SIZE(GetEBP)
+
+ENTRY(GetEIP)
+ pushl %ebp
+ movl %esp,%ebp
+ subl $0x00000004,%esp
+ movl 0x00000004(%ebp),%eax
+ movl %eax,0xfffffffc(%ebp)
+ movl 0xfffffffc(%ebp),%eax
+ leave
+ ret
+SET_SIZE(GetEIP)
+
+ENTRY(GetESP)
+ pushl %ebp
+ movl %esp,%ebp
+ subl $0x00000004,%esp
+ movl %ebp,%eax
+ movl %eax,0xfffffffc(%ebp)
+ movl 0xfffffffc(%ebp),%eax
+ addl $0x00000008,%eax
+ leave
+ ret
+SET_SIZE(GetESP)
+
+
+#elif defined(__sparc)
+
+
+ENTRY(GetPC)
+ save %sp, -120, %sp
+ mov %i7, %i4
+ inccc 8, %i4
+ mov %i4, %i0
+ ret
+ restore
+SET_SIZE(GetPC)
+
+ENTRY(GetSP)
+ save %sp, -120, %sp
+ mov %fp, %i4
+ mov %i4, %i0
+ ret
+ restore
+SET_SIZE(GetSP)
+
+ENTRY(GetFP)
+ save %sp, -120, %sp
+ ld [%fp + 56], %g1
+ mov %g1, %i0
+ ret
+ restore
+SET_SIZE(GetFP)
+
+
+#endif // __i386 || __sparc
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc
new file mode 100644
index 000000000..ff2ea75a8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_sparc.cc: sparc-specific stackwalker.
+//
+// See stackwalker_sparc.h for documentation.
+//
+// Author: Michael Shang
+
+
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+#include "processor/stackwalker_sparc.h"
+
+namespace google_breakpad {
+
+
+StackwalkerSPARC::StackwalkerSPARC(const SystemInfo* system_info,
+ const MDRawContextSPARC* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* resolver_helper)
+ : Stackwalker(system_info, memory, modules, resolver_helper),
+ context_(context) {
+}
+
+
+StackFrame* StackwalkerSPARC::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
+ return NULL;
+ }
+
+ StackFrameSPARC* frame = new StackFrameSPARC();
+
+ // The instruction pointer is stored directly in a register, so pull it
+ // straight out of the CPU context structure.
+ frame->context = *context_;
+ frame->context_validity = StackFrameSPARC::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.pc;
+
+ return frame;
+}
+
+
+StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ StackFrameSPARC* last_frame = static_cast<StackFrameSPARC*>(
+ stack->frames()->back());
+
+ // new: caller
+ // old: callee
+ // %fp, %i6 and g_r[30] is the same, see minidump_format.h
+ // %sp, %o6 and g_r[14] is the same, see minidump_format.h
+ // %sp_new = %fp_old
+ // %fp_new = *(%fp_old + 32 + 32 - 8), where the callee's %i6
+ // %pc_new = *(%fp_old + 32 + 32 - 4) + 8
+ // which is callee's %i7 plus 8
+
+ // A caller frame must reside higher in memory than its callee frames.
+ // Anything else is an error, or an indication that we've reached the
+ // end of the stack.
+ uint64_t stack_pointer = last_frame->context.g_r[30];
+ if (stack_pointer <= last_frame->context.g_r[14]) {
+ return NULL;
+ }
+
+ uint32_t instruction;
+ if (!memory_->GetMemoryAtAddress(stack_pointer + 60,
+ &instruction) || instruction <= 1) {
+ return NULL;
+ }
+
+ uint32_t stack_base;
+ if (!memory_->GetMemoryAtAddress(stack_pointer + 56,
+ &stack_base) || stack_base <= 1) {
+ return NULL;
+ }
+
+ StackFrameSPARC* frame = new StackFrameSPARC();
+
+ frame->context = last_frame->context;
+ frame->context.g_r[14] = stack_pointer;
+ frame->context.g_r[30] = stack_base;
+
+ // frame->context.pc is the return address, which is 2 instruction
+ // past the branch that caused us to arrive at the callee, which are
+ // a CALL instruction then a NOP instruction.
+ // frame_ppc->instruction to 8 less than that. Since all sparc
+ // instructions are 4 bytes wide, this is the address of the branch
+ // instruction. This allows source line information to match up with the
+ // line that contains a function call. Callers that require the exact
+ // return address value may access the %i7/g_r[31] field of StackFrameSPARC.
+ frame->context.pc = instruction + 8;
+ frame->instruction = instruction;
+ frame->context_validity = StackFrameSPARC::CONTEXT_VALID_PC |
+ StackFrameSPARC::CONTEXT_VALID_SP |
+ StackFrameSPARC::CONTEXT_VALID_FP;
+ frame->trust = StackFrame::FRAME_TRUST_FP;
+
+ return frame;
+}
+
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h
new file mode 100644
index 000000000..e8f2a3888
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_sparc.h: sparc-specific stackwalker.
+//
+// Provides stack frames given sparc register context and a memory region
+// corresponding to an sparc stack.
+//
+// Author: Michael Shang
+
+
+#ifndef PROCESSOR_STACKWALKER_SPARC_H__
+#define PROCESSOR_STACKWALKER_SPARC_H__
+
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stackwalker.h"
+
+namespace google_breakpad {
+
+class CodeModules;
+
+class StackwalkerSPARC : public Stackwalker {
+ public:
+ // context is a sparc context object that gives access to sparc-specific
+ // register state corresponding to the innermost called frame to be
+ // included in the stack. The other arguments are passed directly through
+ // to the base Stackwalker constructor.
+ StackwalkerSPARC(const SystemInfo* system_info,
+ const MDRawContextSPARC* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and
+ // stack conventions
+ virtual StackFrame* GetContextFrame();
+ virtual StackFrame* GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextSPARC* context_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STACKWALKER_SPARC_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h
new file mode 100644
index 000000000..ee22a8fe1
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h
@@ -0,0 +1,224 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// Mock classes for writing stackwalker tests, shared amongst architectures.
+
+#ifndef PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_
+#define PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/symbol_supplier.h"
+#include "google_breakpad/processor/system_info.h"
+#include "processor/linked_ptr.h"
+
+class MockMemoryRegion: public google_breakpad::MemoryRegion {
+ public:
+ MockMemoryRegion(): base_address_(0) { }
+
+ // Set this region's address and contents. If we have placed an
+ // instance of this class in a test fixture class, individual tests
+ // can use this to provide the region's contents.
+ void Init(uint64_t base_address, const string &contents) {
+ base_address_ = base_address;
+ contents_ = contents;
+ }
+
+ uint64_t GetBase() const { return base_address_; }
+ uint32_t GetSize() const { return contents_.size(); }
+
+ bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const {
+ return GetMemoryLittleEndian(address, value);
+ }
+ bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const {
+ return GetMemoryLittleEndian(address, value);
+ }
+ bool GetMemoryAtAddress(uint64_t address, uint32_t *value) const {
+ return GetMemoryLittleEndian(address, value);
+ }
+ bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const {
+ return GetMemoryLittleEndian(address, value);
+ }
+ void Print() const {
+ assert(false);
+ }
+
+ private:
+ // Fetch a little-endian value from ADDRESS in contents_ whose size
+ // is BYTES, and store it in *VALUE. Return true on success.
+ template<typename ValueType>
+ bool GetMemoryLittleEndian(uint64_t address, ValueType *value) const {
+ if (address < base_address_ ||
+ address - base_address_ + sizeof(ValueType) > contents_.size())
+ return false;
+ ValueType v = 0;
+ int start = address - base_address_;
+ // The loop condition is odd, but it's correct for size_t.
+ for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--)
+ v = (v << 8) | static_cast<unsigned char>(contents_[start + i]);
+ *value = v;
+ return true;
+ }
+
+ uint64_t base_address_;
+ string contents_;
+};
+
+class MockCodeModule: public google_breakpad::CodeModule {
+ public:
+ MockCodeModule(uint64_t base_address, uint64_t size,
+ const string &code_file, const string &version)
+ : base_address_(base_address), size_(size), code_file_(code_file) { }
+
+ uint64_t base_address() const { return base_address_; }
+ uint64_t size() const { return size_; }
+ string code_file() const { return code_file_; }
+ string code_identifier() const { return code_file_; }
+ string debug_file() const { return code_file_; }
+ string debug_identifier() const { return code_file_; }
+ string version() const { return version_; }
+ google_breakpad::CodeModule *Copy() const {
+ abort(); // Tests won't use this.
+ }
+ virtual uint64_t shrink_down_delta() const { return 0; }
+ virtual void SetShrinkDownDelta(uint64_t shrink_down_delta) {}
+
+ private:
+ uint64_t base_address_;
+ uint64_t size_;
+ string code_file_;
+ string version_;
+};
+
+class MockCodeModules: public google_breakpad::CodeModules {
+ public:
+ typedef google_breakpad::CodeModule CodeModule;
+ typedef google_breakpad::CodeModules CodeModules;
+
+ void Add(const MockCodeModule *module) {
+ modules_.push_back(module);
+ }
+
+ unsigned int module_count() const { return modules_.size(); }
+
+ const CodeModule *GetModuleForAddress(uint64_t address) const {
+ for (ModuleVector::const_iterator i = modules_.begin();
+ i != modules_.end(); i++) {
+ const MockCodeModule *module = *i;
+ if (module->base_address() <= address &&
+ address - module->base_address() < module->size())
+ return module;
+ }
+ return NULL;
+ };
+
+ const CodeModule *GetMainModule() const { return modules_[0]; }
+
+ const CodeModule *GetModuleAtSequence(unsigned int sequence) const {
+ return modules_.at(sequence);
+ }
+
+ const CodeModule *GetModuleAtIndex(unsigned int index) const {
+ return modules_.at(index);
+ }
+
+ CodeModules *Copy() const { abort(); } // Tests won't use this
+
+ virtual std::vector<google_breakpad::linked_ptr<const CodeModule> >
+ GetShrunkRangeModules() const {
+ return std::vector<google_breakpad::linked_ptr<const CodeModule> >();
+ }
+
+ // Returns true, if module address range shrink is enabled.
+ bool IsModuleShrinkEnabled() const {
+ return false;
+ }
+
+ private:
+ typedef std::vector<const MockCodeModule *> ModuleVector;
+ ModuleVector modules_;
+};
+
+class MockSymbolSupplier: public google_breakpad::SymbolSupplier {
+ public:
+ typedef google_breakpad::CodeModule CodeModule;
+ typedef google_breakpad::SystemInfo SystemInfo;
+ MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module,
+ const SystemInfo *system_info,
+ string *symbol_file));
+ MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module,
+ const SystemInfo *system_info,
+ string *symbol_file,
+ string *symbol_data));
+ MOCK_METHOD5(GetCStringSymbolData, SymbolResult(const CodeModule *module,
+ const SystemInfo *system_info,
+ string *symbol_file,
+ char **symbol_data,
+ size_t *symbol_data_size));
+ MOCK_METHOD1(FreeSymbolData, void(const CodeModule *module));
+
+ // Copies the passed string contents into a newly allocated buffer.
+ // The newly allocated buffer will be freed during destruction.
+ char* CopySymbolDataAndOwnTheCopy(const std::string &info,
+ size_t *symbol_data_size) {
+ *symbol_data_size = info.size() + 1;
+ char *symbol_data = new char[*symbol_data_size];
+ memcpy(symbol_data, info.c_str(), info.size());
+ symbol_data[info.size()] = '\0';
+ symbol_data_to_free_.push_back(symbol_data);
+ return symbol_data;
+ }
+
+ virtual ~MockSymbolSupplier() {
+ for (SymbolDataVector::const_iterator i = symbol_data_to_free_.begin();
+ i != symbol_data_to_free_.end(); i++) {
+ char* symbol_data = *i;
+ delete [] symbol_data;
+ }
+ }
+
+ private:
+ // List of symbol data to be freed upon destruction
+ typedef std::vector<char*> SymbolDataVector;
+ SymbolDataVector symbol_data_to_free_;
+};
+
+#endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc
new file mode 100644
index 000000000..29d98e4b8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc
@@ -0,0 +1,672 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_x86.cc: x86-specific stackwalker.
+//
+// See stackwalker_x86.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include <assert.h>
+#include <string>
+
+#include "common/scoped_ptr.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+#include "processor/postfix_evaluator-inl.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/windows_frame_info.h"
+#include "processor/cfi_frame_info.h"
+
+namespace google_breakpad {
+
+// Max reasonable size for a single x86 frame is 128 KB. This value is used in
+// a heuristic for recovering of the EBP chain after a scan for return address.
+// This value is based on a stack frame size histogram built for a set of
+// popular third party libraries which suggests that 99.5% of all frames are
+// smaller than 128 KB.
+static const uint32_t kMaxReasonableGapBetweenFrames = 128 * 1024;
+
+const StackwalkerX86::CFIWalker::RegisterSet
+StackwalkerX86::cfi_register_map_[] = {
+ // It may seem like $eip and $esp are callee-saves, because (with Unix or
+ // cdecl calling conventions) the callee is responsible for having them
+ // restored upon return. But the callee_saves flags here really means
+ // that the walker should assume they're unchanged if the CFI doesn't
+ // mention them, which is clearly wrong for $eip and $esp.
+ { "$eip", ".ra", false,
+ StackFrameX86::CONTEXT_VALID_EIP, &MDRawContextX86::eip },
+ { "$esp", ".cfa", false,
+ StackFrameX86::CONTEXT_VALID_ESP, &MDRawContextX86::esp },
+ { "$ebp", NULL, true,
+ StackFrameX86::CONTEXT_VALID_EBP, &MDRawContextX86::ebp },
+ { "$eax", NULL, false,
+ StackFrameX86::CONTEXT_VALID_EAX, &MDRawContextX86::eax },
+ { "$ebx", NULL, true,
+ StackFrameX86::CONTEXT_VALID_EBX, &MDRawContextX86::ebx },
+ { "$ecx", NULL, false,
+ StackFrameX86::CONTEXT_VALID_ECX, &MDRawContextX86::ecx },
+ { "$edx", NULL, false,
+ StackFrameX86::CONTEXT_VALID_EDX, &MDRawContextX86::edx },
+ { "$esi", NULL, true,
+ StackFrameX86::CONTEXT_VALID_ESI, &MDRawContextX86::esi },
+ { "$edi", NULL, true,
+ StackFrameX86::CONTEXT_VALID_EDI, &MDRawContextX86::edi },
+};
+
+StackwalkerX86::StackwalkerX86(const SystemInfo* system_info,
+ const MDRawContextX86* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ 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]))) {
+ if (memory_ && memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ // The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
+ // Mark memory_ = NULL, which will cause stackwalking to fail.
+ BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
+ HexString(memory_->GetBase()) << "+" <<
+ HexString(memory_->GetSize());
+ memory_ = NULL;
+ }
+}
+
+StackFrameX86::~StackFrameX86() {
+ if (windows_frame_info)
+ delete windows_frame_info;
+ windows_frame_info = NULL;
+ if (cfi_frame_info)
+ delete cfi_frame_info;
+ cfi_frame_info = NULL;
+}
+
+uint64_t StackFrameX86::ReturnAddress() const {
+ assert(context_validity & StackFrameX86::CONTEXT_VALID_EIP);
+ return context.eip;
+}
+
+StackFrame* StackwalkerX86::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
+ return NULL;
+ }
+
+ StackFrameX86* frame = new StackFrameX86();
+
+ // The instruction pointer is stored directly in a register, so pull it
+ // straight out of the CPU context structure.
+ frame->context = *context_;
+ frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.eip;
+
+ return frame;
+}
+
+StackFrameX86* StackwalkerX86::GetCallerByWindowsFrameInfo(
+ const vector<StackFrame*> &frames,
+ WindowsFrameInfo* last_frame_info,
+ bool stack_scan_allowed) {
+ StackFrame::FrameTrust trust = StackFrame::FRAME_TRUST_NONE;
+
+ StackFrameX86* last_frame = static_cast<StackFrameX86*>(frames.back());
+
+ // Save the stack walking info we found, in case we need it later to
+ // find the callee of the frame we're constructing now.
+ last_frame->windows_frame_info = last_frame_info;
+
+ // This function only covers the full STACK WIN case. If
+ // last_frame_info is VALID_PARAMETER_SIZE-only, then we should
+ // assume the traditional frame format or use some other strategy.
+ if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL)
+ return NULL;
+
+ // This stackwalker sets each frame's %esp to its value immediately prior
+ // to the CALL into the callee. This means that %esp points to the last
+ // callee argument pushed onto the stack, which may not be where %esp points
+ // after the callee returns. Specifically, the value is correct for the
+ // cdecl calling convention, but not other conventions. The cdecl
+ // convention requires a caller to pop its callee's arguments from the
+ // stack after the callee returns. This is usually accomplished by adding
+ // the known size of the arguments to %esp. Other calling conventions,
+ // including stdcall, thiscall, and fastcall, require the callee to pop any
+ // parameters stored on the stack before returning. This is usually
+ // accomplished by using the RET n instruction, which pops n bytes off
+ // the stack after popping the return address.
+ //
+ // Because each frame's %esp will point to a location on the stack after
+ // callee arguments have been PUSHed, when locating things in a stack frame
+ // relative to %esp, the size of the arguments to the callee need to be
+ // taken into account. This seems a little bit unclean, but it's better
+ // than the alternative, which would need to take these same things into
+ // account, but only for cdecl functions. With this implementation, we get
+ // to be agnostic about each function's calling convention. Furthermore,
+ // this is how Windows debugging tools work, so it means that the %esp
+ // values produced by this stackwalker directly correspond to the %esp
+ // values you'll see there.
+ //
+ // If the last frame has no callee (because it's the context frame), just
+ // set the callee parameter size to 0: the stack pointer can't point to
+ // callee arguments because there's no callee. This is correct as long
+ // as the context wasn't captured while arguments were being pushed for
+ // a function call. Note that there may be functions whose parameter sizes
+ // are unknown, 0 is also used in that case. When that happens, it should
+ // be possible to walk to the next frame without reference to %esp.
+
+ uint32_t last_frame_callee_parameter_size = 0;
+ int frames_already_walked = frames.size();
+ if (frames_already_walked >= 2) {
+ const StackFrameX86* last_frame_callee
+ = static_cast<StackFrameX86*>(frames[frames_already_walked - 2]);
+ WindowsFrameInfo* last_frame_callee_info
+ = last_frame_callee->windows_frame_info;
+ if (last_frame_callee_info &&
+ (last_frame_callee_info->valid
+ & WindowsFrameInfo::VALID_PARAMETER_SIZE)) {
+ last_frame_callee_parameter_size =
+ last_frame_callee_info->parameter_size;
+ }
+ }
+
+ // Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used
+ // in each program string, and their previous values are known, so set them
+ // here.
+ PostfixEvaluator<uint32_t>::DictionaryType dictionary;
+ // Provide the current register values.
+ dictionary["$ebp"] = last_frame->context.ebp;
+ dictionary["$esp"] = last_frame->context.esp;
+ // Provide constants from the debug info for last_frame and its callee.
+ // .cbCalleeParams is a Breakpad extension that allows us to use the
+ // PostfixEvaluator engine when certain types of debugging information
+ // are present without having to write the constants into the program
+ // string as literals.
+ dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
+ dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
+ dictionary[".cbLocals"] = last_frame_info->local_size;
+
+ uint32_t raSearchStart = last_frame->context.esp +
+ last_frame_callee_parameter_size +
+ last_frame_info->local_size +
+ last_frame_info->saved_register_size;
+
+ uint32_t raSearchStartOld = raSearchStart;
+ uint32_t found = 0; // dummy value
+ // Scan up to three words above the calculated search value, in case
+ // the stack was aligned to a quadword boundary.
+ //
+ // TODO(ivan.penkov): Consider cleaning up the scan for return address that
+ // follows. The purpose of this scan is to adjust the .raSearchStart
+ // calculation (which is based on register %esp) in the cases where register
+ // %esp may have been aligned (up to a quadword). There are two problems
+ // with this approach:
+ // 1) In practice, 64 byte boundary alignment is seen which clearly can not
+ // be handled by a three word scan.
+ // 2) A search for a return address is "guesswork" by definition because
+ // the results will be different depending on what is left on the stack
+ // from previous executions.
+ // So, basically, the results from this scan should be ignored if other means
+ // for calculation of the value of .raSearchStart are available.
+ if (ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3) &&
+ last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT &&
+ last_frame->windows_frame_info != NULL &&
+ last_frame_info->type_ == WindowsFrameInfo::STACK_INFO_FPO &&
+ raSearchStartOld == raSearchStart &&
+ found == last_frame->context.eip) {
+ // The context frame represents an FPO-optimized Windows system call.
+ // On the top of the stack we have a pointer to the current instruction.
+ // This means that the callee has returned but the return address is still
+ // on the top of the stack which is very atypical situaltion.
+ // Skip one slot from the stack and do another scan in order to get the
+ // actual return address.
+ raSearchStart += 4;
+ ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3);
+ }
+
+ dictionary[".cbParams"] = last_frame_info->parameter_size;
+
+ // Decide what type of program string to use. The program string is in
+ // postfix notation and will be passed to PostfixEvaluator::Evaluate.
+ // Given the dictionary and the program string, it is possible to compute
+ // the return address and the values of other registers in the calling
+ // function. Because of bugs described below, the stack may need to be
+ // scanned for these values. The results of program string evaluation
+ // will be used to determine whether to scan for better values.
+ string program_string;
+ bool recover_ebp = true;
+
+ trust = StackFrame::FRAME_TRUST_CFI;
+ if (!last_frame_info->program_string.empty()) {
+ // The FPO data has its own program string, which will tell us how to
+ // get to the caller frame, and may even fill in the values of
+ // nonvolatile registers and provide pointers to local variables and
+ // parameters. In some cases, particularly with program strings that use
+ // .raSearchStart, the stack may need to be scanned afterward.
+ program_string = last_frame_info->program_string;
+ } else if (last_frame_info->allocates_base_pointer) {
+ // The function corresponding to the last frame doesn't use the frame
+ // pointer for conventional purposes, but it does allocate a new
+ // frame pointer and use it for its own purposes. Its callee's
+ // information is still accessed relative to %esp, and the previous
+ // value of %ebp can be recovered from a location in its stack frame,
+ // within the saved-register area.
+ //
+ // Functions that fall into this category use the %ebp register for
+ // a purpose other than the frame pointer. They restore the caller's
+ // %ebp before returning. These functions create their stack frame
+ // after a CALL by decrementing the stack pointer in an amount
+ // sufficient to store local variables, and then PUSHing saved
+ // registers onto the stack. Arguments to a callee function, if any,
+ // are PUSHed after that. Walking up to the caller, therefore,
+ // can be done solely with calculations relative to the stack pointer
+ // (%esp). The return address is recovered from the memory location
+ // above the known sizes of the callee's parameters, saved registers,
+ // and locals. The caller's stack pointer (the value of %esp when
+ // the caller executed CALL) is the location immediately above the
+ // saved return address. The saved value of %ebp to be restored for
+ // the caller is at a known location in the saved-register area of
+ // the stack frame.
+ //
+ // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
+ // link-time code generation mode (/LTCG and /GL) can generate erroneous
+ // debugging data. The reported size of saved registers can be 0,
+ // which is clearly an error because these frames must, at the very
+ // least, save %ebp. For this reason, in addition to those given above
+ // about the use of .raSearchStart, the stack may need to be scanned
+ // for a better return address and a better frame pointer after the
+ // program string is evaluated.
+ //
+ // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
+ // %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
+ // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
+ program_string = "$eip .raSearchStart ^ = "
+ "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = "
+ "$esp .raSearchStart 4 + =";
+ } else {
+ // The function corresponding to the last frame doesn't use %ebp at
+ // all. The callee frame is located relative to %esp.
+ //
+ // The called procedure's instruction pointer and stack pointer are
+ // recovered in the same way as the case above, except that no
+ // frame pointer (%ebp) is used at all, so it is not saved anywhere
+ // in the callee's stack frame and does not need to be recovered.
+ // Because %ebp wasn't used in the callee, whatever value it has
+ // is the value that it had in the caller, so it can be carried
+ // straight through without bringing its validity into question.
+ //
+ // Because of the use of .raSearchStart, the stack will possibly be
+ // examined to locate a better return address after program string
+ // evaluation. The stack will not be examined to locate a saved
+ // %ebp value, because these frames do not save (or use) %ebp.
+ //
+ // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
+ // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
+ // %ebp_new = %ebp_old
+ program_string = "$eip .raSearchStart ^ = "
+ "$esp .raSearchStart 4 + =";
+ recover_ebp = false;
+ }
+
+ // Check for alignment operators in the program string. If alignment
+ // operators are found, then current %ebp must be valid and it is the only
+ // reliable data point that can be used for getting to the previous frame.
+ // E.g. the .raSearchStart calculation (above) is based on %esp and since
+ // %esp was aligned in the current frame (which is a lossy operation) the
+ // calculated value of .raSearchStart cannot be correct and should not be
+ // used. Instead .raSearchStart must be calculated based on %ebp.
+ // The code that follows assumes that .raSearchStart is supposed to point
+ // at the saved return address (ebp + 4).
+ // For some more details on this topic, take a look at the following thread:
+ // https://groups.google.com/forum/#!topic/google-breakpad-dev/ZP1FA9B1JjM
+ if ((StackFrameX86::CONTEXT_VALID_EBP & last_frame->context_validity) != 0 &&
+ program_string.find('@') != string::npos) {
+ raSearchStart = last_frame->context.ebp + 4;
+ }
+
+ // The difference between raSearch and raSearchStart is unknown,
+ // but making them the same seems to work well in practice.
+ dictionary[".raSearchStart"] = raSearchStart;
+ dictionary[".raSearch"] = raSearchStart;
+
+ // Now crank it out, making sure that the program string set at least the
+ // two required variables.
+ PostfixEvaluator<uint32_t> evaluator =
+ PostfixEvaluator<uint32_t>(&dictionary, memory_);
+ PostfixEvaluator<uint32_t>::DictionaryValidityType dictionary_validity;
+ if (!evaluator.Evaluate(program_string, &dictionary_validity) ||
+ dictionary_validity.find("$eip") == dictionary_validity.end() ||
+ dictionary_validity.find("$esp") == dictionary_validity.end()) {
+ // Program string evaluation failed. It may be that %eip is not somewhere
+ // with stack frame info, and %ebp is pointing to non-stack memory, so
+ // our evaluation couldn't succeed. We'll scan the stack for a return
+ // address. This can happen if the stack is in a module for which
+ // we don't have symbols, and that module is compiled without a
+ // frame pointer.
+ uint32_t location_start = last_frame->context.esp;
+ uint32_t location, eip;
+ if (!stack_scan_allowed
+ || !ScanForReturnAddress(location_start, &location, &eip,
+ frames.size() == 1 /* is_context_frame */)) {
+ // if we can't find an instruction pointer even with stack scanning,
+ // give up.
+ return NULL;
+ }
+
+ // This seems like a reasonable return address. Since program string
+ // evaluation failed, use it and set %esp to the location above the
+ // one where the return address was found.
+ dictionary["$eip"] = eip;
+ dictionary["$esp"] = location + 4;
+ trust = StackFrame::FRAME_TRUST_SCAN;
+ }
+
+ // Since this stack frame did not use %ebp in a traditional way,
+ // locating the return address isn't entirely deterministic. In that
+ // case, the stack can be scanned to locate the return address.
+ //
+ // However, if program string evaluation resulted in both %eip and
+ // %ebp values of 0, trust that the end of the stack has been
+ // reached and don't scan for anything else.
+ if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) {
+ int offset = 0;
+
+ // This scan can only be done if a CodeModules object is available, to
+ // check that candidate return addresses are in fact inside a module.
+ //
+ // TODO(mmentovai): This ignores dynamically-generated code. One possible
+ // solution is to check the minidump's memory map to see if the candidate
+ // %eip value comes from a mapped executable page, although this would
+ // require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad
+ // client doesn't currently write (it would need to call MiniDumpWriteDump
+ // with the MiniDumpWithFullMemoryInfo type bit set). Even given this
+ // ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce
+ // an independent execute privilege on memory pages.
+
+ uint32_t eip = dictionary["$eip"];
+ if (modules_ && !modules_->GetModuleForAddress(eip)) {
+ // The instruction pointer at .raSearchStart was invalid, so start
+ // looking one 32-bit word above that location.
+ uint32_t location_start = dictionary[".raSearchStart"] + 4;
+ uint32_t location;
+ if (stack_scan_allowed
+ && ScanForReturnAddress(location_start, &location, &eip,
+ frames.size() == 1 /* is_context_frame */)) {
+ // This is a better return address that what program string
+ // evaluation found. Use it, and set %esp to the location above the
+ // one where the return address was found.
+ dictionary["$eip"] = eip;
+ dictionary["$esp"] = location + 4;
+ offset = location - location_start;
+ trust = StackFrame::FRAME_TRUST_CFI_SCAN;
+ }
+ }
+
+ if (recover_ebp) {
+ // When trying to recover the previous value of the frame pointer (%ebp),
+ // start looking at the lowest possible address in the saved-register
+ // area, and look at the entire saved register area, increased by the
+ // size of |offset| to account for additional data that may be on the
+ // stack. The scan is performed from the highest possible address to
+ // the lowest, because the expectation is that the function's prolog
+ // would have saved %ebp early.
+ uint32_t ebp = dictionary["$ebp"];
+
+ // When a scan for return address is used, it is possible to skip one or
+ // more frames (when return address is not in a known module). One
+ // indication for skipped frames is when the value of %ebp is lower than
+ // the location of the return address on the stack
+ bool has_skipped_frames =
+ (trust != StackFrame::FRAME_TRUST_CFI && ebp <= raSearchStart + offset);
+
+ uint32_t value; // throwaway variable to check pointer validity
+ if (has_skipped_frames || !memory_->GetMemoryAtAddress(ebp, &value)) {
+ int fp_search_bytes = last_frame_info->saved_register_size + offset;
+ uint32_t location_end = last_frame->context.esp +
+ last_frame_callee_parameter_size;
+
+ for (uint32_t location = location_end + fp_search_bytes;
+ location >= location_end;
+ location -= 4) {
+ if (!memory_->GetMemoryAtAddress(location, &ebp))
+ break;
+
+ if (memory_->GetMemoryAtAddress(ebp, &value)) {
+ // The candidate value is a pointer to the same memory region
+ // (the stack). Prefer it as a recovered %ebp result.
+ dictionary["$ebp"] = ebp;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameX86* frame = new StackFrameX86();
+
+ frame->trust = trust;
+ frame->context = last_frame->context;
+ frame->context.eip = dictionary["$eip"];
+ frame->context.esp = dictionary["$esp"];
+ frame->context.ebp = dictionary["$ebp"];
+ frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP;
+
+ // These are nonvolatile (callee-save) registers, and the program string
+ // may have filled them in.
+ if (dictionary_validity.find("$ebx") != dictionary_validity.end()) {
+ frame->context.ebx = dictionary["$ebx"];
+ frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
+ }
+ if (dictionary_validity.find("$esi") != dictionary_validity.end()) {
+ frame->context.esi = dictionary["$esi"];
+ frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
+ }
+ if (dictionary_validity.find("$edi") != dictionary_validity.end()) {
+ frame->context.edi = dictionary["$edi"];
+ frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
+ }
+
+ return frame;
+}
+
+StackFrameX86* StackwalkerX86::GetCallerByCFIFrameInfo(
+ const vector<StackFrame*> &frames,
+ CFIFrameInfo* cfi_frame_info) {
+ StackFrameX86* last_frame = static_cast<StackFrameX86*>(frames.back());
+ last_frame->cfi_frame_info = cfi_frame_info;
+
+ scoped_ptr<StackFrameX86> frame(new StackFrameX86());
+ if (!cfi_walker_
+ .FindCallerRegisters(*memory_, *cfi_frame_info,
+ last_frame->context, last_frame->context_validity,
+ &frame->context, &frame->context_validity))
+ return NULL;
+
+ // Make sure we recovered all the essentials.
+ static const int essentials = (StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP);
+ if ((frame->context_validity & essentials) != essentials)
+ return NULL;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+
+ return frame.release();
+}
+
+StackFrameX86* StackwalkerX86::GetCallerByEBPAtBase(
+ const vector<StackFrame*> &frames,
+ bool stack_scan_allowed) {
+ StackFrame::FrameTrust trust;
+ StackFrameX86* last_frame = static_cast<StackFrameX86*>(frames.back());
+ uint32_t last_esp = last_frame->context.esp;
+ uint32_t last_ebp = last_frame->context.ebp;
+
+ // Assume that the standard %ebp-using x86 calling convention is in
+ // use.
+ //
+ // The typical x86 calling convention, when frame pointers are present,
+ // is for the calling procedure to use CALL, which pushes the return
+ // address onto the stack and sets the instruction pointer (%eip) to
+ // the entry point of the called routine. The called routine then
+ // PUSHes the calling routine's frame pointer (%ebp) onto the stack
+ // before copying the stack pointer (%esp) to the frame pointer (%ebp).
+ // Therefore, the calling procedure's frame pointer is always available
+ // by dereferencing the called procedure's frame pointer, and the return
+ // address is always available at the memory location immediately above
+ // the address pointed to by the called procedure's frame pointer. The
+ // calling procedure's stack pointer (%esp) is 8 higher than the value
+ // of the called procedure's frame pointer at the time the calling
+ // procedure made the CALL: 4 bytes for the return address pushed by the
+ // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
+ // pointer.
+ //
+ // %eip_new = *(%ebp_old + 4)
+ // %esp_new = %ebp_old + 8
+ // %ebp_new = *(%ebp_old)
+
+ uint32_t caller_eip, caller_esp, caller_ebp;
+
+ if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) &&
+ memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) {
+ caller_esp = last_ebp + 8;
+ trust = StackFrame::FRAME_TRUST_FP;
+ } else {
+ // We couldn't read the memory %ebp refers to. It may be that %ebp
+ // is pointing to non-stack memory. We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols, and that module
+ // is compiled without a frame pointer.
+ if (!stack_scan_allowed
+ || !ScanForReturnAddress(last_esp, &caller_esp, &caller_eip,
+ frames.size() == 1 /* is_context_frame */)) {
+ // if we can't find an instruction pointer even with stack scanning,
+ // give up.
+ return NULL;
+ }
+
+ // ScanForReturnAddress found a reasonable return address. Advance %esp to
+ // the location immediately above the one where the return address was
+ // found.
+ caller_esp += 4;
+ // Try to restore the %ebp chain. The caller %ebp should be stored at a
+ // location immediately below the one where the return address was found.
+ // A valid caller %ebp must be greater than the address where it is stored
+ // and the gap between the two adjacent frames should be reasonable.
+ uint32_t restored_ebp_chain = caller_esp - 8;
+ if (!memory_->GetMemoryAtAddress(restored_ebp_chain, &caller_ebp) ||
+ caller_ebp <= restored_ebp_chain ||
+ caller_ebp - restored_ebp_chain > kMaxReasonableGapBetweenFrames) {
+ // The restored %ebp chain doesn't appear to be valid.
+ // Assume that %ebp is unchanged.
+ caller_ebp = last_ebp;
+ }
+
+ trust = StackFrame::FRAME_TRUST_SCAN;
+ }
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameX86* frame = new StackFrameX86();
+
+ frame->trust = trust;
+ frame->context = last_frame->context;
+ frame->context.eip = caller_eip;
+ frame->context.esp = caller_esp;
+ frame->context.ebp = caller_ebp;
+ frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP;
+
+ return frame;
+}
+
+StackFrame* StackwalkerX86::GetCallerFrame(const CallStack* stack,
+ 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();
+ StackFrameX86* last_frame = static_cast<StackFrameX86*>(frames.back());
+ scoped_ptr<StackFrameX86> new_frame;
+
+ // If the resolver has Windows stack walking information, use that.
+ WindowsFrameInfo* windows_frame_info
+ = frame_symbolizer_->FindWindowsFrameInfo(last_frame);
+ if (windows_frame_info)
+ new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info,
+ stack_scan_allowed));
+
+ // If the resolver has DWARF CFI information, use that.
+ if (!new_frame.get()) {
+ CFIFrameInfo* cfi_frame_info =
+ frame_symbolizer_->FindCFIFrameInfo(last_frame);
+ if (cfi_frame_info)
+ new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info));
+ }
+
+ // Otherwise, hope that the program was using a traditional frame structure.
+ if (!new_frame.get())
+ new_frame.reset(GetCallerByEBPAtBase(frames, stack_scan_allowed));
+
+ // If nothing worked, tell the caller.
+ if (!new_frame.get())
+ return NULL;
+
+ // Treat an instruction address of 0 as end-of-stack.
+ if (new_frame->context.eip == 0)
+ return NULL;
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (new_frame->context.esp <= last_frame->context.esp)
+ return NULL;
+
+ // new_frame->context.eip is the return address, which is 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.eip - 1;
+
+ return new_frame.release();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h
new file mode 100644
index 000000000..0659a13bf
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h
@@ -0,0 +1,117 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_x86.h: x86-specific stackwalker.
+//
+// Provides stack frames given x86 register context and a memory region
+// corresponding to an x86 stack.
+//
+// Author: Mark Mentovai
+
+
+#ifndef PROCESSOR_STACKWALKER_X86_H__
+#define PROCESSOR_STACKWALKER_X86_H__
+
+#include <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 StackwalkerX86 : public Stackwalker {
+ public:
+ // context is an x86 context object that gives access to x86-specific
+ // register state corresponding to the innermost called frame to be
+ // included in the stack. The other arguments are passed directly through
+ // to the base Stackwalker constructor.
+ StackwalkerX86(const SystemInfo* system_info,
+ const MDRawContextX86* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // A STACK CFI-driven frame walker for the X86.
+ typedef SimpleCFIWalker<uint32_t, MDRawContextX86> CFIWalker;
+
+ // Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
+ // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
+ // alternate conventions as guided by any WindowsFrameInfo available for the
+ // code in question.).
+ virtual StackFrame* GetContextFrame();
+ virtual StackFrame* GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed);
+
+ // Use windows_frame_info (derived from STACK WIN and FUNC records)
+ // to construct the frame that called frames.back(). The caller
+ // takes ownership of the returned frame. Return NULL on failure.
+ StackFrameX86* GetCallerByWindowsFrameInfo(
+ const vector<StackFrame*> &frames,
+ WindowsFrameInfo* windows_frame_info,
+ 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.
+ StackFrameX86* GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
+ CFIFrameInfo* cfi_frame_info);
+
+ // Assuming a traditional frame layout --- where the caller's %ebp
+ // has been pushed just after the return address and the callee's
+ // %ebp points to the saved %ebp --- construct the frame that called
+ // frames.back(). The caller takes ownership of the returned frame.
+ // Return NULL on failure.
+ StackFrameX86* GetCallerByEBPAtBase(const vector<StackFrame*> &frames,
+ bool stack_scan_allowed);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextX86* context_;
+
+ // Our register map, for cfi_walker_.
+ static const CFIWalker::RegisterSet cfi_register_map_[];
+
+ // Our CFI frame walker.
+ const CFIWalker cfi_walker_;
+};
+
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STACKWALKER_X86_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc
new file mode 100644
index 000000000..d4c61c8c4
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc
@@ -0,0 +1,2128 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class.
+
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/test_assembler.h"
+#include "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_x86.h"
+#include "processor/windows_frame_info.h"
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CallStack;
+using google_breakpad::CodeModule;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameX86;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerX86;
+using google_breakpad::SystemInfo;
+using google_breakpad::WindowsFrameInfo;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+class StackwalkerX86Fixture {
+ public:
+ StackwalkerX86Fixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x40000000, 0x10000, "module1", "version1"),
+ module2(0x50000000, 0x10000, "module2", "version2"),
+ module3(0x771d0000, 0x180000, "module3", "version3"),
+ module4(0x75f90000, 0x46000, "module4", "version4"),
+ module5(0x75730000, 0x110000, "module5", "version5"),
+ module6(0x647f0000, 0x1ba8000, "module6", "version6") {
+ // Identify the system as a Linux system.
+ system_info.os = "Linux";
+ system_info.os_short = "linux";
+ system_info.os_version = "Salacious Skink";
+ system_info.cpu = "x86";
+ system_info.cpu_info = "";
+
+ // Put distinctive values in the raw CPU context.
+ BrandContext(&raw_context);
+
+ // Create some modules with some stock debugging information.
+ modules.Add(&module1);
+ modules.Add(&module2);
+ modules.Add(&module3);
+ modules.Add(&module4);
+ modules.Add(&module5);
+ modules.Add(&module6);
+
+ // 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(MDRawContextX86 *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;
+ MDRawContextX86 raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModule module3;
+ MockCodeModule module4;
+ MockCodeModule module5;
+ MockCodeModule module6;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame *> *frames;
+};
+
+class SanityCheck: public StackwalkerX86Fixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0); // end-of-stack marker
+ RegionFromSection();
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerX86 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();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetContextFrame: public StackwalkerX86Fixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ stack_section.start() = 0x80000000;
+ stack_section.D32(0).D32(0); // end-of-stack marker
+ RegionFromSection();
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
+class GetCallerFrame: public StackwalkerX86Fixture, public Test {
+ protected:
+ void IPAddressIsNotInKnownModuleTestImpl(bool has_corrupt_symbols);
+};
+
+// Walk a traditional frame. A traditional frame saves the caller's
+// %ebp just below the return address, and has its own %ebp pointing
+// at the saved %ebp.
+TEST_F(GetCallerFrame, Traditional) {
+ stack_section.start() = 0x80000000;
+ Label frame0_ebp, frame1_ebp;
+ stack_section
+ .Append(12, 0) // frame 0: space
+ .Mark(&frame0_ebp) // frame 0 %ebp points here
+ .D32(frame1_ebp) // frame 0: saved %ebp
+ .D32(0x40008679) // frame 0: return address
+ .Append(8, 0) // frame 1: space
+ .Mark(&frame1_ebp) // frame 1 %ebp points here
+ .D32(0) // frame 1: saved %ebp (stack end)
+ .D32(0); // frame 1: return address (stack end)
+ RegionFromSection();
+ raw_context.eip = 0x4000c7a5;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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_EQ(2U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000c7a5U, frame0->instruction);
+ EXPECT_EQ(0x4000c7a5U, frame0->context.eip);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(NULL, frame0->windows_frame_info);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x40008679U, frame1->instruction + 1);
+ EXPECT_EQ(0x40008679U, frame1->context.eip);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Walk a traditional frame, but use a bogus %ebp value, forcing a scan
+// of the stack for something that looks like a return address.
+TEST_F(GetCallerFrame, TraditionalScan) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ Label frame1_esp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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_EQ(2U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000f49dU, frame0->instruction);
+ EXPECT_EQ(0x4000f49dU, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
+ EXPECT_EQ(NULL, frame0->windows_frame_info);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
+ EXPECT_EQ(0x4000129dU, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Force scanning for a return address a long way down the stack
+TEST_F(GetCallerFrame, TraditionalScanLongWay) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ Label frame1_esp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .Append(20 * 4, 0) // a bunch of space
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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_EQ(2U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000f49dU, frame0->instruction);
+ EXPECT_EQ(0x4000f49dU, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
+ EXPECT_EQ(NULL, frame0->windows_frame_info);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
+ EXPECT_EQ(0x4000129dU, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Test that set_max_frames_scanned prevents using stack scanning
+// to find caller frames.
+TEST_F(GetCallerFrame, ScanningNotAllowed) {
+ stack_section.start() = 0x80000000;
+ Label frame1_ebp;
+ stack_section
+ // frame 0
+ .D32(0xf065dc76) // locals area:
+ .D32(0x46ee2167) // garbage that doesn't look like
+ .D32(0xbab023ec) // a return address
+ .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
+ .D32(0x4000129d) // return address
+ // frame 1
+ .Append(8, 0) // space
+ .Mark(&frame1_ebp) // %ebp points here
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // return address (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000f49d;
+ raw_context.esp = stack_section.start().Value();
+ // Make the frame pointer bogus, to make the stackwalker scan the stack
+ // for something that looks like a return address.
+ raw_context.ebp = 0xd43eed6e;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000f49dU, frame0->instruction);
+ EXPECT_EQ(0x4000f49dU, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
+ EXPECT_EQ(NULL, frame0->windows_frame_info);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a stack frame.
+TEST_F(GetCallerFrame, WindowsFrameData) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
+ " $T2 $esp .cbSavedRegs + ="
+ " $T0 .raSearchStart ="
+ " $eip $T0 ^ ="
+ " $esp $T0 4 + ="
+ " $ebx $T2 4 - ^ ="
+ " $edi $T2 8 - ^ ="
+ " $esi $T2 12 - ^ ="
+ " $ebp $T2 16 - ^ =\n");
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .D32(frame1_ebp) // saved regs: %ebp
+ .D32(0xa7120d1a) // %esi
+ .D32(0x630891be) // %edi
+ .D32(0x9068a878) // %ebx
+ .D32(0xa08ea45f) // locals: unused
+ .D32(0x40001350) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(12, 0) // empty space
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000aa85;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = 0xf052c1de; // should not be needed to walk frame
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000aa85U, frame0->instruction);
+ EXPECT_EQ(0x4000aa85U, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(0xf052c1deU, frame0->context.ebp);
+ EXPECT_TRUE(frame0->windows_frame_info != NULL);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP
+ | StackFrameX86::CONTEXT_VALID_EBX
+ | StackFrameX86::CONTEXT_VALID_ESI
+ | StackFrameX86::CONTEXT_VALID_EDI),
+ frame1->context_validity);
+ EXPECT_EQ(0x40001350U, frame1->instruction + 1);
+ EXPECT_EQ(0x40001350U, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(0x9068a878U, frame1->context.ebx);
+ EXPECT_EQ(0xa7120d1aU, frame1->context.esi);
+ EXPECT_EQ(0x630891beU, frame1->context.edi);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a stack frame where the stack
+// is aligned and we must search
+TEST_F(GetCallerFrame, WindowsFrameDataAligned) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 aa85 176 0 0 4 4 8 0 1"
+ " $T1 .raSearch ="
+ " $T0 $T1 4 - 8 @ ="
+ " $ebp $T1 4 - ^ ="
+ " $eip $T1 ^ ="
+ " $esp $T1 4 + =");
+ Label frame0_esp, frame0_ebp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .Mark(&frame0_esp)
+ .D32(0x0ffa0ffa) // unused saved register
+ .D32(0xdeaddead) // locals
+ .D32(0xbeefbeef)
+ .D32(0) // 8-byte alignment
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // saved %ebp
+ .D32(0x5000129d) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .D32(0x1) // parameter
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000aa85;
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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("module2", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000aa85U, frame0->instruction);
+ EXPECT_EQ(0x4000aa85U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_TRUE(frame0->windows_frame_info != NULL);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x5000129dU, frame1->instruction + 1);
+ EXPECT_EQ(0x5000129dU, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a frame, and depend on the
+// parameter size from the callee as well.
+TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) {
+ SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n");
+ SetModuleSymbols(&module2,
+ // Note bogus parameter size in FUNC record; the stack walker
+ // should prefer the STACK WIN record, and see '4' below.
+ "FUNC aa85 176 beef module2::whine\n"
+ "STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
+ " $T2 $esp .cbLocals + .cbSavedRegs + ="
+ " $T0 .raSearchStart ="
+ " $eip $T0 ^ ="
+ " $esp $T0 4 + ="
+ " $ebp $T0 20 - ^ ="
+ " $ebx $T0 8 - ^ =\n");
+ Label frame0_esp, frame0_ebp;
+ Label frame1_esp;
+ Label frame2_esp, frame2_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. Traditional frame.
+ .Mark(&frame0_esp)
+ .Append(16, 0) // frame space
+ .Mark(&frame0_ebp)
+ .D32(0x6fa902e0) // saved %ebp. Not a frame pointer.
+ .D32(0x5000aa95) // return address, in module2::whine
+ // frame 1, in module2::whine. FrameData frame.
+ .Mark(&frame1_esp)
+ .D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle
+ .D32(0xbdc92f9f) // argument 2
+ .D32(0x0b1d8442) // argument 1
+ .D32(frame2_ebp) // saved %ebp
+ .D32(0xb1b90a15) // unused
+ .D32(0xf18e072d) // unused
+ .D32(0x2558c7f3) // saved %ebx
+ .D32(0x0365e25e) // unused
+ .D32(0x2a179e38) // return address; $T0 points here
+ // frame 2, in no module
+ .Mark(&frame2_esp)
+ .Append(12, 0) // empty space
+ .Mark(&frame2_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x40001004; // in module1::wheedle
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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(3U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x40001004U, frame0->instruction);
+ EXPECT_EQ(0x40001004U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&module1, frame0->module);
+ EXPECT_EQ("module1::wheedle", frame0->function_name);
+ EXPECT_EQ(0x40001000U, frame0->function_base);
+ // The FUNC record for module1::wheedle should have produced a
+ // WindowsFrameInfo structure with only the parameter size valid.
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
+ frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_UNKNOWN,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x5000aa95U, frame1->instruction + 1);
+ EXPECT_EQ(0x5000aa95U, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(0x6fa902e0U, frame1->context.ebp);
+ EXPECT_EQ(&module2, frame1->module);
+ EXPECT_EQ("module2::whine", frame1->function_name);
+ EXPECT_EQ(0x5000aa85U, frame1->function_base);
+ ASSERT_TRUE(frame1->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame1->windows_frame_info->type_);
+ // This should not see the 0xbeef parameter size from the FUNC
+ // record, but should instead see the STACK WIN record.
+ EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP
+ | StackFrameX86::CONTEXT_VALID_EBX),
+ frame2->context_validity);
+ EXPECT_EQ(0x2a179e38U, frame2->instruction + 1);
+ EXPECT_EQ(0x2a179e38U, frame2->context.eip);
+ EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
+ EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(0x2558c7f3U, frame2->context.ebx);
+ EXPECT_EQ(NULL, frame2->module);
+ EXPECT_EQ(NULL, frame2->windows_frame_info);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a stack frame, where the
+// expression fails to yield both an $eip and an $ebp value, and the stack
+// walker must scan.
+TEST_F(GetCallerFrame, WindowsFrameDataScan) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n");
+ // Mark frame 1's PC as the end of the stack.
+ SetModuleSymbols(&module2,
+ "FUNC 7c38 accf 0 module2::function\n"
+ "STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n");
+ Label frame1_esp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0
+ .Append(16, 0x2a) // unused, garbage
+ .D32(0x50007ce9) // return address
+ // frame 1
+ .Mark(&frame1_esp)
+ .Append(8, 0); // empty space
+
+ RegionFromSection();
+ raw_context.eip = 0x40000c9c;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x40000c9cU, frame0->instruction);
+ EXPECT_EQ(0x40000c9cU, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(0x2ae314cdU, frame0->context.ebp);
+ EXPECT_TRUE(frame0->windows_frame_info != NULL);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker
+ // does not actually fetch the EBP after a scan (forcing the next frame
+ // to be scanned as well). But let's grandfather the existing behavior in
+ // for now.
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x50007ce9U, frame1->instruction + 1);
+ EXPECT_EQ(0x50007ce9U, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_TRUE(frame1->windows_frame_info != NULL);
+ }
+}
+
+// Use Windows frame data (a "STACK WIN 4" record, from a
+// FrameTypeFrameData DIA record) to walk a stack frame, where the
+// expression yields an $eip that falls outside of any module, and the
+// stack walker must scan.
+TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) {
+ SetModuleSymbols(&module1,
+ "STACK WIN 4 6e6 e7 0 0 0 8 4 0 1"
+ // A traditional frame, actually.
+ " $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n");
+ // Mark frame 1's PC as the end of the stack.
+ SetModuleSymbols(&module2,
+ "FUNC cfdb 8406 0 module2::function\n"
+ "STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n");
+ stack_section.start() = 0x80000000;
+
+ // In this stack, the context's %ebp is pointing at the wrong place, so
+ // the stack walker needs to scan to find the return address, and then
+ // scan again to find the caller's saved %ebp.
+ Label frame0_ebp, frame1_ebp, frame1_esp;
+ stack_section
+ // frame 0
+ .Append(8, 0x2a) // garbage
+ .Mark(&frame0_ebp) // frame 0 %ebp points here, but should point
+ // at *** below
+ // The STACK WIN record says that the following two values are
+ // frame 1's saved %ebp and return address, but the %ebp is wrong;
+ // they're garbage. The stack walker will scan for the right values.
+ .D32(0x3d937b2b) // alleged to be frame 1's saved %ebp
+ .D32(0x17847f5b) // alleged to be frame 1's return address
+ .D32(frame1_ebp) // frame 1's real saved %ebp; scan will find
+ .D32(0x2b2b2b2b) // first word of realigned register save area
+ // *** frame 0 %ebp ought to be pointing here
+ .D32(0x2c2c2c2c) // realigned locals area
+ .D32(0x5000d000) // frame 1's real saved %eip; scan will find
+ // Frame 1, in module2::function. The STACK WIN record describes
+ // this as the oldest frame, without referring to its contents, so
+ // we needn't to provide any actual data here.
+ .Mark(&frame1_esp)
+ .Mark(&frame1_ebp) // frame 1 %ebp points here
+ // A dummy value for frame 1's %ebp to point at. The scan recognizes the
+ // saved %ebp because it points to a valid word in the stack memory region.
+ .D32(0x2d2d2d2d);
+
+ RegionFromSection();
+ raw_context.eip = 0x40000700;
+ raw_context.esp = stack_section.start().Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x40000700U, frame0->instruction);
+ EXPECT_EQ(0x40000700U, frame0->context.eip);
+ EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_TRUE(frame0->windows_frame_info != NULL);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
+ // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
+ // walker does not actually fetch the EBP after a scan (forcing the
+ // next frame to be scanned as well). But let's grandfather the existing
+ // behavior in for now.
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x5000d000U, frame1->instruction + 1);
+ EXPECT_EQ(0x5000d000U, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_TRUE(frame1->windows_frame_info != NULL);
+ }
+}
+
+// Use Windows FrameTypeFPO data to walk a stack frame for a function that
+// does not modify %ebp from the value it had in the caller.
+TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) {
+ SetModuleSymbols(&module1,
+ // Note bogus parameter size in FUNC record; the walker
+ // should prefer the STACK WIN record, and see the '8' below.
+ "FUNC e8a8 100 feeb module1::discombobulated\n"
+ "STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n");
+ Label frame0_esp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
+ .Mark(&frame0_esp)
+ // no outgoing parameters; this is the youngest frame.
+ .D32(0x7c521352) // four bytes of saved registers
+ .Append(0x10, 0x42) // local area
+ .D32(0x40009b5b) // return address, in module1, no function
+ // frame 1, in module1, no function.
+ .Mark(&frame1_esp)
+ .D32(0xf60ea7fc) // junk
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4000e8b8; // in module1::whine
+ raw_context.esp = stack_section.start().Value();
+ // Frame pointer unchanged from caller.
+ raw_context.ebp = frame1_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x4000e8b8U, frame0->instruction);
+ EXPECT_EQ(0x4000e8b8U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ // unchanged from caller
+ EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&module1, frame0->module);
+ EXPECT_EQ("module1::discombobulated", frame0->function_name);
+ EXPECT_EQ(0x4000e8a8U, frame0->function_base);
+ // The STACK WIN record for module1::discombobulated should have
+ // produced a fully populated WindowsFrameInfo structure.
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x40009b5bU, frame1->instruction + 1);
+ EXPECT_EQ(0x40009b5bU, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&module1, frame1->module);
+ EXPECT_EQ("", frame1->function_name);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// Use Windows FrameTypeFPO data to walk a stack frame for a function
+// that uses %ebp for its own purposes, saving the value it had in the
+// caller in the standard place in the saved register area.
+TEST_F(GetCallerFrame, WindowsFPOUsedEBP) {
+ SetModuleSymbols(&module1,
+ // Note bogus parameter size in FUNC record; the walker
+ // should prefer the STACK WIN record, and see the '8' below.
+ "FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n"
+ "STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n");
+ Label frame0_esp;
+ Label frame1_esp, frame1_ebp;
+ stack_section.start() = 0x80000000;
+ stack_section
+ // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
+ .Mark(&frame0_esp)
+ // no outgoing parameters; this is the youngest frame.
+ .D32(frame1_ebp) // saved register area: saved %ebp
+ .D32(0xb68bd5f9) // saved register area: something else
+ .D32(0xd25d05fc) // local area
+ .D32(0x4000debe) // return address, in module1, no function
+ // frame 1, in module1, no function.
+ .Mark(&frame1_esp)
+ .D32(0xf0c9a974) // junk
+ .Mark(&frame1_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens
+ raw_context.esp = stack_section.start().Value();
+ // RaisedByTheAliens uses %ebp for its own mysterious purposes.
+ raw_context.ebp = 0xecbdd1a5;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x40009ab8U, frame0->instruction);
+ EXPECT_EQ(0x40009ab8U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(0xecbdd1a5, frame0->context.ebp);
+ EXPECT_EQ(&module1, frame0->module);
+ EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name);
+ EXPECT_EQ(0x40009aa8U, frame0->function_base);
+ // The STACK WIN record for module1::RaisedByTheAliens should have
+ // produced a fully populated WindowsFrameInfo structure.
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ("", frame0->windows_frame_info->program_string);
+ EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x4000debeU, frame1->instruction + 1);
+ EXPECT_EQ(0x4000debeU, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&module1, frame1->module);
+ EXPECT_EQ("", frame1->function_name);
+ EXPECT_EQ(NULL, frame1->windows_frame_info);
+ }
+}
+
+// This is a regression unit test which covers a bug which has to do with
+// FPO-optimized Windows system call stubs in the context frame. There is
+// a more recent Windows system call dispatch mechanism which differs from
+// the one which is being tested here. The newer system call dispatch
+// mechanism creates an extra context frame (KiFastSystemCallRet).
+TEST_F(GetCallerFrame, WindowsFPOSystemCall) {
+ SetModuleSymbols(&module3, // ntdll.dll
+ "PUBLIC 1f8ac c ZwWaitForSingleObject\n"
+ "STACK WIN 0 1f8ac 1b 0 0 c 0 0 0 0 0\n");
+ SetModuleSymbols(&module4, // kernelbase.dll
+ "PUBLIC 109f9 c WaitForSingleObjectEx\n"
+ "PUBLIC 36590 0 _except_handler4\n"
+ "STACK WIN 4 109f9 df c 0 c c 48 0 1 $T0 $ebp = $eip "
+ "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
+ "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"
+ "STACK WIN 4 36590 154 17 0 10 0 14 0 1 $T0 $ebp = $eip "
+ "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 "
+ ".cbSavedRegs - = $P $T0 8 + .cbParams + =\n");
+ SetModuleSymbols(&module5, // kernel32.dll
+ "PUBLIC 11136 8 WaitForSingleObject\n"
+ "PUBLIC 11151 c WaitForSingleObjectExImplementation\n"
+ "STACK WIN 4 11136 16 5 0 8 0 0 0 1 $T0 $ebp = $eip "
+ "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
+ "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"
+ "STACK WIN 4 11151 7a 5 0 c 0 0 0 1 $T0 $ebp = $eip "
+ "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
+ "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n");
+ SetModuleSymbols(&module6, // chrome.dll
+ "FILE 7038 some_file_name.h\n"
+ "FILE 839776 some_file_name.cc\n"
+ "FUNC 217fda 17 4 function_217fda\n"
+ "217fda 4 102 839776\n"
+ "FUNC 217ff1 a 4 function_217ff1\n"
+ "217ff1 0 594 7038\n"
+ "217ff1 a 596 7038\n"
+ "STACK WIN 0 217ff1 a 0 0 4 0 0 0 0 0\n");
+
+ Label frame0_esp, frame1_esp;
+ Label frame1_ebp, frame2_ebp, frame3_ebp;
+ stack_section.start() = 0x002ff290;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(0x771ef8c1) // EIP in frame 0 (system call)
+ .D32(0x75fa0a91) // return address of frame 0
+ .Mark(&frame1_esp)
+ .D32(0x000017b0) // args to child
+ .D32(0x00000000)
+ .D32(0x002ff2d8)
+ .D32(0x88014a2e)
+ .D32(0x002ff364)
+ .D32(0x000017b0)
+ .D32(0x00000000)
+ .D32(0x00000024)
+ .D32(0x00000001)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x9e3b9800)
+ .D32(0xfffffff7)
+ .D32(0x00000000)
+ .D32(0x002ff2a4)
+ .D32(0x64a07ff1) // random value to be confused with a return address
+ .D32(0x002ff8dc)
+ .D32(0x75fc6590) // random value to be confused with a return address
+ .D32(0xfdd2c6ea)
+ .D32(0x00000000)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP
+ .D32(0x75741194) // return address of frame 1
+ .D32(0x000017b0) // args to child
+ .D32(0x0036ee80)
+ .D32(0x00000000)
+ .D32(0x65bc7d14)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child EBP
+ .D32(0x75741148) // return address of frame 2
+ .D32(0x000017b0) // args to child
+ .D32(0x0036ee80)
+ .D32(0x00000000)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x771ef8c1; // in ntdll::ZwWaitForSingleObject
+ raw_context.esp = stack_section.start().Value();
+ ASSERT_TRUE(raw_context.esp == frame0_esp.Value());
+ raw_context.ebp = frame1_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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(4U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x771ef8c1U, frame0->instruction);
+ EXPECT_EQ(0x771ef8c1U, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&module3, frame0->module);
+ EXPECT_EQ("ZwWaitForSingleObject", frame0->function_name);
+ // The STACK WIN record for module3!ZwWaitForSingleObject should have
+ // produced a fully populated WindowsFrameInfo structure.
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ("", frame0->windows_frame_info->program_string);
+ EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
+ | StackFrameX86::CONTEXT_VALID_ESP
+ | StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x75fa0a91U, frame1->instruction + 1);
+ EXPECT_EQ(0x75fa0a91U, frame1->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&module4, frame1->module);
+ EXPECT_EQ("WaitForSingleObjectEx", frame1->function_name);
+ // The STACK WIN record for module4!WaitForSingleObjectEx should have
+ // produced a fully populated WindowsFrameInfo structure.
+ ASSERT_TRUE(frame1->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame1->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L "
+ "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Scan the stack for a better return address and potentially skip frames
+// when the calculated return address is not in a known module. Note, that
+// the span of this scan is somewhat arbitrarily limited to 160 search words
+// for the context frame and 40 search words (pointers) for the other frames:
+// const int kRASearchWords = 40;
+// This means that frames can be skipped only when their size is relatively
+// small: smaller than 4 * kRASearchWords * sizeof(InstructionType)
+TEST_F(GetCallerFrame, ReturnAddressIsNotInKnownModule) {
+ MockCodeModule msvcrt_dll(0x77be0000, 0x58000, "msvcrt.dll", "version1");
+ SetModuleSymbols(&msvcrt_dll, // msvcrt.dll
+ "PUBLIC 38180 0 wcsstr\n"
+ "STACK WIN 4 38180 61 10 0 8 0 0 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule kernel32_dll(0x7c800000, 0x103000, "kernel32.dll", "version1");
+ SetModuleSymbols(&kernel32_dll, // kernel32.dll
+ "PUBLIC efda 8 FindNextFileW\n"
+ "STACK WIN 4 efda 1bb c 0 8 8 3c 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule chrome_dll(0x1c30000, 0x28C8000, "chrome.dll", "version1");
+ SetModuleSymbols(&chrome_dll, // chrome.dll
+ "FUNC e3cff 4af 0 file_util::FileEnumerator::Next()\n"
+ "e3cff 1a 711 2505\n"
+ "STACK WIN 4 e3cff 4af 20 0 4 c 94 0 1 $T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =\n");
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&msvcrt_dll);
+ local_modules.Add(&kernel32_dll);
+ local_modules.Add(&chrome_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_ebp;
+ Label frame2_ebp;
+ Label frame3_ebp;
+
+ stack_section.start() = 0x0932f2d0;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(0x0764e000)
+ .D32(0x0764e068)
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child EBP
+ .D32(0x001767a0) // return address of frame 0
+ // Not in known module
+ .D32(0x0764e0c6)
+ .D32(0x001bb1b8)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x07578828)
+ .D32(0x0764e000)
+ .D32(0x00000000)
+ .D32(0x001c0010)
+ .D32(0x0764e0c6)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP
+ .D32(0x7c80f10f) // return address of frame 1
+ // inside kernel32!FindNextFileW
+ .D32(0x000008f8)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f34c)
+ .D32(0x0764e000)
+ .D32(0x00001000)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f6a8)
+ .D32(0x00000000)
+ .D32(0x0932f6d8)
+ .D32(0x00000000)
+ .D32(0x000000d6)
+ .D32(0x0764e000)
+ .D32(0x7ff9a000)
+ .D32(0x0932f3fc)
+ .D32(0x00000001)
+ .D32(0x00000001)
+ .D32(0x07578828)
+ .D32(0x0000002e)
+ .D32(0x0932f340)
+ .D32(0x0932eef4)
+ .D32(0x0932ffdc)
+ .D32(0x7c839ad8)
+ .D32(0x7c80f0d8)
+ .D32(0x00000000)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child EBP
+ .D32(0x01d13f91) // return address of frame 2
+ // inside chrome_dll!file_util::FileEnumerator::Next
+ .D32(0x07578828)
+ .D32(0x0932f6ac)
+ .D32(0x0932f9c4)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x00000003)
+ .D32(0x0932f978)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x07f30000)
+ .D32(0x01c3ba17)
+ .D32(0x08bab840)
+ .D32(0x07f31580)
+ .D32(0x00000000)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x0932f40c)
+ .D32(0x01d13b53)
+ .D32(0x0932f958)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x00000000)
+ .D32(0x0932f6ac)
+ .D32(0x01e13ef0)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f958)
+ .D32(0x08bab840)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x0932f9b4)
+ .D32(0x000000a7)
+ .D32(0x000000a7)
+ .D32(0x0932f998)
+ .D32(0x579627a2)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x77c181cd; // inside msvcrt!wcsstr
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+ // sanity
+ ASSERT_TRUE(raw_context.esp == stack_section.start().Value());
+ ASSERT_TRUE(raw_context.ebp == stack_section.start().Value() + 8);
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x77c181cdU, frame0->instruction);
+ EXPECT_EQ(0x77c181cdU, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&msvcrt_dll, frame0->module);
+ EXPECT_EQ("wcsstr", frame0->function_name);
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =",
+ frame0->windows_frame_info->program_string);
+ // It has program string, so allocates_base_pointer is not expected
+ EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x7c80f10fU, frame1->instruction + 1);
+ EXPECT_EQ(0x7c80f10fU, frame1->context.eip);
+ // frame 1 was skipped, so intead of frame1_ebp compare with frame2_ebp.
+ EXPECT_EQ(frame2_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&kernel32_dll, frame1->module);
+ EXPECT_EQ("FindNextFileW", frame1->function_name);
+ ASSERT_TRUE(frame1->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame1->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame2->context_validity);
+ EXPECT_EQ(0x01d13f91U, frame2->instruction + 1);
+ EXPECT_EQ(0x01d13f91U, frame2->context.eip);
+ // frame 1 was skipped, so intead of frame2_ebp compare with frame3_ebp.
+ EXPECT_EQ(frame3_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame2->module);
+ EXPECT_EQ("file_util::FileEnumerator::Next()", frame2->function_name);
+ ASSERT_TRUE(frame2->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame2->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =",
+ frame2->windows_frame_info->program_string);
+ EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Test the .raSearchStart/.raSearch calculation when alignment operators are
+// used in the program string. The current %ebp must be valid and it is the
+// only reliable data point that can be used for that calculation.
+TEST_F(GetCallerFrame, HandleAlignmentInProgramString) {
+ MockCodeModule chrome_dll(0x59630000, 0x19e3000, "chrome.dll", "version1");
+ SetModuleSymbols(&chrome_dll, // chrome.dll
+ "FUNC 56422 50c 8 base::MessageLoop::RunTask"
+ "(base::PendingTask const &)\n"
+ "56422 e 458 4589\n"
+ "STACK WIN 4 56422 50c 11 0 8 c ac 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$20 $T0 176 - ^ = $23 $T0 180 - ^ = $24 $T0 184 - ^ =\n"
+ "FUNC 55d34 34a 0 base::MessageLoop::DoWork()\n"
+ "55d34 11 596 4589\n"
+ "STACK WIN 4 55d34 34a 19 0 0 c 134 0 1 $T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 312 - ^ = $23 $T0 316 - ^ = $24 $T0 "
+ "320 - ^ =\n"
+ "FUNC 55c39 fb 0 base::MessagePumpForIO::DoRunLoop()\n"
+ "55c39 d 518 19962\n"
+ "STACK WIN 4 55c39 fb d 0 0 c 34 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 64 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + "
+ "= $20 $T0 56 - ^ = $23 $T0 60 - ^ = $24 $T0 64 - ^ =\n"
+ "FUNC 55bf0 49 4 base::MessagePumpWin::Run(base::"
+ "MessagePump::Delegate *)\n"
+ "55bf0 49 48 4724\n"
+ "STACK WIN 4 55bf0 49 c 0 4 0 10 0 1 $T0 $ebp = $eip $T0 4 "
+ "+ ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "FUNC 165d de 4 malloc\n"
+ "165d 6 119 54\n"
+ "STACK WIN 4 165d de d 0 4 8 0 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 "
+ "+ = $23 $T0 4 - ^ = $24 $T0 8 - ^ =\n"
+ "FUNC 55ac9 79 0 base::MessageLoop::RunInternal()\n"
+ "55ac9 d 427 4589\n"
+ "STACK WIN 4 55ac9 79 d 0 0 8 10 0 1 $T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$23 $T0 20 - ^ = $24 $T0 24 - ^ =\n");
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&chrome_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_esp;
+ Label frame1_ebp;
+ Label frame2_esp;
+ Label frame2_ebp;
+ Label frame3_esp;
+ Label frame3_ebp;
+
+ stack_section.start() = 0x046bfc80;
+ stack_section
+ .D32(0)
+ .Mark(&frame0_esp)
+ .D32(0x01e235a0)
+ .D32(0x00000000)
+ .D32(0x01e9f580)
+ .D32(0x01e9f580)
+ .D32(0x00000020)
+ .D32(0x00000000)
+ .D32(0x00463674)
+ .D32(0x00000020)
+ .D32(0x00000000)
+ .D32(0x046bfcd8)
+ .D32(0x046bfcd8)
+ .D32(0x0001204b)
+ .D32(0x00000000)
+ .D32(0xfdddb523)
+ .D32(0x00000000)
+ .D32(0x00000007)
+ .D32(0x00000040)
+ .D32(0x00000000)
+ .D32(0x59631693) // chrome_59630000!malloc+0x36
+ .D32(0x01e9f580)
+ .D32(0x01e9f580)
+ .D32(0x046bfcf8)
+ .D32(0x77da6704) // ntdll!NtSetIoCompletion+0xc
+ .D32(0x046bfd4c)
+ .D32(0x59685bec) // chrome_59630000!base::MessageLoop::StartHistogrammer..
+ .D32(0x01e235a0)
+
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child EBP .D32(0x046bfd0c)
+ .D32(0x59685c2e) // Return address in
+ // chrome_59630000!base::MessagePumpWin::Run+0x3e
+ .Mark(&frame1_esp)
+ .D32(0x01e75a90)
+ .D32(0x046bfd4c)
+ .D32(0x01e75a90)
+ .D32(0x00000000)
+ .D32(0x00000300)
+ .D32(0x00000001)
+
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP .D32(0x046bfd30)
+ .D32(0x59685b3c) // Return address in
+ // chrome_59630000!base::MessageLoop::RunInternal+0x73
+ .Mark(&frame2_esp)
+ .D32(0x01e75a90)
+ .D32(0x00000000)
+ .D32(0x046bfd4c)
+ .D32(0x59658123) // chrome_59630000!std::deque..
+ .D32(0x046bfda0)
+ .D32(0x01e79d70)
+ .D32(0x046bfda0)
+
+ .Mark(&frame2_ebp) // .D32(0x046bfd40)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x59685c46; // Context frame in
+ // base::MessagePumpForIO::DoRunLoop
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame->context_validity);
+ EXPECT_EQ("base::MessagePumpForIO::DoRunLoop()", frame->function_name);
+ EXPECT_EQ(0x59685c46U, frame->instruction);
+ EXPECT_EQ(0x59685c46U, frame->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = $T0 "
+ "$T1 4 - 64 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + "
+ "= $20 $T0 56 - ^ = $23 $T0 60 - ^ = $24 $T0 64 - ^ =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame->context_validity);
+ EXPECT_EQ("base::MessagePumpWin::Run(base::MessagePump::Delegate *)",
+ frame->function_name);
+ EXPECT_EQ(1500011566U, frame->instruction + 1);
+ EXPECT_EQ(1500011566U, frame->context.eip);
+ EXPECT_EQ(frame1_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame1_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame->context_validity);
+ EXPECT_EQ("base::MessageLoop::RunInternal()", frame->function_name);
+ EXPECT_EQ(1500011324U, frame->instruction + 1);
+ EXPECT_EQ(1500011324U, frame->context.eip);
+ EXPECT_EQ(frame2_esp.Value(), frame->context.esp);
+ EXPECT_EQ(frame2_ebp.Value(), frame->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame->module);
+ ASSERT_TRUE(frame->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = $T0 "
+ "$T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp $T1 4 + = "
+ "$23 $T0 20 - ^ = $24 $T0 24 - ^ =",
+ frame->windows_frame_info->program_string);
+ EXPECT_FALSE(frame->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Scan the stack for a return address and potentially skip frames when the
+// current IP address is not in a known module. Note, that that the span of
+// this scan is limited to 120 search words for the context frame and 30
+// search words (pointers) for the other frames:
+// const int kRASearchWords = 30;
+void GetCallerFrame::IPAddressIsNotInKnownModuleTestImpl(
+ bool has_corrupt_symbols) {
+ MockCodeModule remoting_core_dll(0x54080000, 0x501000, "remoting_core.dll",
+ "version1");
+ string symbols_func_section =
+ "FUNC 137214 17d 10 PK11_Verify\n"
+ "FUNC 15c834 37 14 nsc_ECDSAVerifyStub\n"
+ "FUNC 1611d3 91 14 NSC_Verify\n"
+ "FUNC 162ff7 60 4 sftk_SessionFromHandle\n";
+ string symbols_stack_section =
+ "STACK WIN 4 137214 17d 9 0 10 0 10 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 15c834 37 6 0 14 0 18 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 1611d3 91 7 0 14 0 8 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n"
+ "STACK WIN 4 162ff7 60 5 0 4 0 0 0 1 $T0 $ebp = "
+ "$eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =\n";
+
+ string symbols = symbols_func_section;
+ if (has_corrupt_symbols) {
+ symbols.append(string(1, '\0')); // null terminator in the middle
+ symbols.append("\n");
+ symbols.append("FUNC 1234\n" // invalid FUNC records
+ "FUNNC 1234\n"
+ "STACK WIN 4 1234 234 23 " // invalid STACK record
+ "23423423 234 23 234 234 "
+ "234 23 234 23 234 234 "
+ "234 234 234\n");
+ }
+ symbols.append(symbols_stack_section);
+ SetModuleSymbols(&remoting_core_dll, symbols);
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&remoting_core_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_ebp;
+ Label frame1_esp;
+ Label frame2_ebp;
+ Label frame2_esp;
+ Label frame3_ebp;
+ Label frame3_esp;
+ Label bogus_stack_location_1;
+ Label bogus_stack_location_2;
+ Label bogus_stack_location_3;
+
+ stack_section.start() = 0x01a3ea28;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(bogus_stack_location_2)
+ .D32(bogus_stack_location_1)
+ .D32(0x042478e4)
+ .D32(bogus_stack_location_2)
+ .D32(0x00000000)
+ .D32(0x041f0420)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000001)
+ .D32(0x00b7e0d0)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000001)
+ .D32(0x00b7f570)
+ .Mark(&bogus_stack_location_1)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x04289530)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7e910)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7d998)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7dec0)
+ .Mark(&bogus_stack_location_2)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x04289428)
+ .D32(0x00000000)
+ .D32(0x00000040)
+ .D32(0x00000008)
+ .D32(0x00b7f258)
+ .Mark(&bogus_stack_location_3)
+ .D32(0x00000000)
+ .D32(0x041f3560)
+ .D32(0x00000041)
+ .D32(0x00000020)
+ .D32(0xffffffff)
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child %ebp
+ .D32(0x541dc866) // return address of frame 0
+ // inside remoting_core!nsc_ECDSAVerifyStub+0x32
+ .Mark(&frame1_esp)
+ .D32(0x04247860)
+ .D32(0x01a3eaec)
+ .D32(0x01a3eaf8)
+ .D32(0x541e304f) // remoting_core!sftk_SessionFromHandle+0x58
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x01a3eb2c)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child %ebp
+ .D32(0x541e1234) // return address of frame 1
+ // inside remoting_core!NSC_Verify+0x61
+ .Mark(&frame2_esp)
+ .D32(0x04247858)
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .D32(0x01000005)
+ .D32(0x00b2f7a0)
+ .D32(0x041f0420)
+ .D32(0x041f3650)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child %ebp
+ .D32(0x541b734d) // return address of frame 1
+ // inside remoting_core!PK11_Verify+0x139
+ .Mark(&frame3_esp)
+ .D32(0x01000005)
+ .D32(0x01a3ec08)
+ .D32(0x00000014)
+ .D32(0x0404c620)
+ .D32(0x00000040)
+ .D32(0x04073e00)
+ .D32(0x04073e00)
+ .D32(0x04247050)
+ .D32(0x00001041)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x4247860; // IP address not in known module
+ raw_context.ebp = 0x5420362d; // bogus
+ raw_context.esp = frame0_esp.Value();
+
+ // sanity
+ ASSERT_TRUE(raw_context.esp == stack_section.start().Value());
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+ if (has_corrupt_symbols) {
+ ASSERT_EQ(1U, modules_with_corrupt_symbols.size());
+ ASSERT_EQ("remoting_core.dll",
+ modules_with_corrupt_symbols[0]->debug_file());
+ } else {
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ }
+ frames = call_stack.frames();
+
+ ASSERT_EQ(4U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(raw_context.eip, frame0->context.eip);
+ EXPECT_EQ(raw_context.ebp, frame0->context.ebp);
+ EXPECT_EQ(raw_context.esp, frame0->context.esp);
+ EXPECT_EQ(NULL, frame0->module); // IP not in known module
+ EXPECT_EQ("", frame0->function_name);
+ ASSERT_EQ(NULL, frame0->windows_frame_info);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame1->module);
+ EXPECT_EQ("nsc_ECDSAVerifyStub", frame1->function_name);
+ ASSERT_TRUE(frame1->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame1->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame2->context_validity);
+ EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame2->module);
+ EXPECT_EQ("NSC_Verify", frame2->function_name);
+ ASSERT_TRUE(frame2->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame2->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame2->windows_frame_info->program_string);
+ EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame3 = static_cast<StackFrameX86 *>(frames->at(3));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame3->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame3->context_validity);
+ EXPECT_EQ(frame3_ebp.Value(), frame3->context.ebp);
+ EXPECT_EQ(frame3_esp.Value(), frame3->context.esp);
+ EXPECT_EQ(&remoting_core_dll, frame3->module);
+ EXPECT_EQ("PK11_Verify", frame3->function_name);
+ ASSERT_TRUE(frame3->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame3->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame3->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =",
+ frame3->windows_frame_info->program_string);
+ EXPECT_FALSE(frame3->windows_frame_info->allocates_base_pointer);
+ }
+}
+
+// Runs IPAddressIsNotInKnownModule test with good symbols
+TEST_F(GetCallerFrame, IPAddressIsNotInKnownModule) {
+ IPAddressIsNotInKnownModuleTestImpl(false /* has_corrupt_modules */);
+}
+
+// Runs IPAddressIsNotInKnownModule test with corrupt symbols
+TEST_F(GetCallerFrame, IPAddressIsNotInKnownModule_CorruptSymbols) {
+ IPAddressIsNotInKnownModuleTestImpl(true /* has_corrupt_modules */);
+}
+
+struct CFIFixture: public StackwalkerX86Fixture {
+ CFIFixture() {
+ // Provide a bunch of STACK CFI records; individual tests walk to the
+ // caller from every point in this series, expecting to find the same
+ // set of register values.
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 10 enchiridion\n"
+ // Initially, just a return address.
+ "STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n"
+ // Push %ebx.
+ "STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n"
+ // Move %esi into %ebx. Weird, but permitted.
+ "STACK CFI 4002 $esi: $ebx\n"
+ // Allocate frame space, and save %edi.
+ "STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n"
+ // Put the return address in %edi.
+ "STACK CFI 4005 .ra: $edi\n"
+ // Save %ebp, and use it as a frame pointer.
+ "STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n"
+
+ // The calling function.
+ "FUNC 5000 1000 10 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n");
+
+ // Provide some distinctive values for the caller's registers.
+ expected.esp = 0x80000000;
+ expected.eip = 0x40005510;
+ expected.ebp = 0xc0d4aab9;
+ expected.ebx = 0x60f20ce6;
+ expected.esi = 0x53d1379d;
+ expected.edi = 0xafbae234;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set
+ // raw_context.esp to the stack's starting address.) Expect two
+ // stack frames; in the older frame, expect the callee-saves
+ // registers to have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.esp = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 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());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x40004000U, frame0->function_base);
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
+ frame0->windows_frame_info->valid);
+ ASSERT_TRUE(frame0->cfi_frame_info != NULL);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP |
+ StackFrameX86::CONTEXT_VALID_EBX |
+ StackFrameX86::CONTEXT_VALID_ESI |
+ StackFrameX86::CONTEXT_VALID_EDI),
+ frame1->context_validity);
+ EXPECT_EQ(expected.eip, frame1->context.eip);
+ EXPECT_EQ(expected.esp, frame1->context.esp);
+ EXPECT_EQ(expected.ebp, frame1->context.ebp);
+ EXPECT_EQ(expected.ebx, frame1->context.ebx);
+ EXPECT_EQ(expected.esi, frame1->context.esi);
+ EXPECT_EQ(expected.edi, frame1->context.edi);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ }
+ }
+
+ // The values the stack walker should find for the caller's registers.
+ MDRawContextX86 expected;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, At4000) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004000;
+ CheckWalk();
+}
+
+TEST_F(CFI, At4001) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004001;
+ raw_context.ebx = 0x91aa9a8b; // callee's %ebx value
+ CheckWalk();
+}
+
+TEST_F(CFI, At4002) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004002;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0xa5c790ed; // callee's %esi value
+ CheckWalk();
+}
+
+TEST_F(CFI, At4003) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0x56ec3db7) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x53d67131) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004003;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0xa97f229d; // callee's %esi
+ raw_context.edi = 0xb05cc997; // callee's %edi
+ CheckWalk();
+}
+
+// The results here should be the same as those at module offset
+// 0x4003.
+TEST_F(CFI, At4004) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xe29782c2) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x5ba29ce9) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x40005510) // return address
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004004;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x0fb7dc4e; // callee's %esi
+ raw_context.edi = 0x993b4280; // callee's %edi
+ CheckWalk();
+}
+
+TEST_F(CFI, At4005) {
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xe29782c2) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0x5ba29ce9) // garbage
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x8036cc02) // garbage
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004005;
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x0fb7dc4e; // callee's %esi
+ raw_context.edi = 0x40005510; // return address
+ CheckWalk();
+}
+
+TEST_F(CFI, At4006) {
+ Label frame0_ebp;
+ Label frame1_esp = expected.esp;
+ stack_section
+ .D32(0xdcdd25cd) // garbage
+ .D32(0xafbae234) // saved %edi
+ .D32(0xc0d4aab9) // saved %ebp
+ .Mark(&frame0_ebp) // frame pointer points here
+ .D32(0x60f20ce6) // saved %ebx
+ .D32(0x8036cc02) // garbage
+ .Mark(&frame1_esp); // This effectively sets stack_section.start().
+ raw_context.eip = 0x40004006;
+ raw_context.ebp = frame0_ebp.Value();
+ raw_context.ebx = 0x53d1379d; // saved %esi
+ raw_context.esi = 0x743833c9; // callee's %esi
+ raw_context.edi = 0x40005510; // return address
+ CheckWalk();
+}
+
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_address_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map-inl.h
new file mode 100644
index 000000000..67e07976e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map-inl.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_address_map-inl.h: StaticAddressMap implementation.
+//
+// See static_address_map.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_ADDRESS_MAP_INL_H__
+#define PROCESSOR_STATIC_ADDRESS_MAP_INL_H__
+
+#include "processor/static_address_map.h"
+
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+bool StaticAddressMap<AddressType, EntryType>::Retrieve(
+ const AddressType &address,
+ const EntryType *&entry, AddressType *entry_address) const {
+
+ // upper_bound gives the first element whose key is greater than address,
+ // but we want the first element whose key is less than or equal to address.
+ // Decrement the iterator to get there, but not if the upper_bound already
+ // points to the beginning of the map - in that case, address is lower than
+ // the lowest stored key, so return false.
+
+ MapConstIterator iterator = map_.upper_bound(address);
+ if (iterator == map_.begin())
+ return false;
+ --iterator;
+
+ entry = iterator.GetValuePtr();
+ // Make sure AddressType is a copyable basic type
+ if (entry_address)
+ *entry_address = iterator.GetKey();
+
+ return true;
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_ADDRESS_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_address_map.h b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map.h
new file mode 100644
index 000000000..6bafc6675
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_address_map.h: StaticAddressMap.
+//
+// StaticAddressMap is a wrapper class of StaticMap, just as AddressMap wraps
+// std::map. StaticAddressMap provides read-only Retrieve() operation, similar
+// as AddressMap. However, the difference between StaticAddressMap and
+// AddressMap is that StaticAddressMap does not support dynamic operation
+// Store() due to the static nature of the underlying StaticMap.
+//
+// See address_map.h for reference.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_ADDRESS_MAP_H__
+#define PROCESSOR_STATIC_ADDRESS_MAP_H__
+
+#include "processor/static_map-inl.h"
+
+namespace google_breakpad {
+
+// AddressType MUST be a basic type, e.g.: integer types etc
+// EntryType could be a complex type, so we retrieve its pointer instead.
+template<typename AddressType, typename EntryType>
+class StaticAddressMap {
+ public:
+ StaticAddressMap(): map_() { }
+ explicit StaticAddressMap(const char *map_data): map_(map_data) { }
+
+ // Locates the entry stored at the highest address less than or equal to
+ // the address argument. If there is no such range, returns false. The
+ // entry is returned in entry, which is a required argument. If
+ // entry_address is not NULL, it will be set to the address that the entry
+ // was stored at.
+ bool Retrieve(const AddressType &address,
+ const EntryType *&entry, AddressType *entry_address) const;
+
+ private:
+ friend class ModuleComparer;
+ // Convenience types.
+ typedef StaticAddressMap* SelfPtr;
+ typedef StaticMap<AddressType, EntryType> AddressToEntryMap;
+ typedef typename AddressToEntryMap::const_iterator MapConstIterator;
+
+ AddressToEntryMap map_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_ADDRESS_MAP_H__
+
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_address_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map_unittest.cc
new file mode 100644
index 000000000..12c735cff
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_address_map_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_address_map_unittest.cc: Unit tests for StaticAddressMap.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <climits>
+#include <cstdlib>
+#include <ctime>
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include "breakpad_googletest_includes.h"
+#include "common/using_std_string.h"
+#include "processor/address_map-inl.h"
+#include "processor/static_address_map-inl.h"
+#include "processor/simple_serializer-inl.h"
+#include "map_serializers-inl.h"
+
+typedef google_breakpad::StaticAddressMap<int, char> TestMap;
+typedef google_breakpad::AddressMap<int, string> AddrMap;
+
+class TestStaticAddressMap : public ::testing::Test {
+ protected:
+ void SetUp() {
+ for (int testcase = 0; testcase < kNumberTestCases; ++testcase) {
+ testdata[testcase] = new int[testsize[testcase]];
+ }
+
+ // Test data set0: NULL (empty map)
+
+ // Test data set1: single element.
+ testdata[1][0] = 10;
+
+ // Test data set2: six elements.
+ const int tempdata[] = {5, 10, 14, 15, 16, 20};
+ for (int i = 0; i < testsize[2]; ++i)
+ testdata[2][i] = tempdata[i];
+
+ // Test data set3:
+ srand(time(NULL));
+ for (int i = 0; i < testsize[3]; ++i)
+ testdata[3][i] = rand();
+
+ // Setup maps.
+ std::stringstream sstream;
+ for (int testcase = 0; testcase < kNumberTestCases; ++testcase) {
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ sstream.clear();
+ sstream << "test " << testdata[testcase][data_item];
+ addr_map[testcase].Store(testdata[testcase][data_item], sstream.str());
+ }
+ map_data[testcase] = serializer.Serialize(addr_map[testcase], NULL);
+ test_map[testcase] = TestMap(map_data[testcase]);
+ }
+ }
+
+ void TearDown() {
+ for (int i = 0; i < kNumberTestCases; ++i) {
+ delete [] map_data[i];
+ delete [] testdata[i];
+ }
+ }
+
+ void CompareRetrieveResult(int testcase, int target) {
+ int address;
+ int address_test;
+ string entry;
+ string entry_test;
+ const char *entry_cstring = NULL;
+ bool found;
+ bool found_test;
+
+ found = addr_map[testcase].Retrieve(target, &entry, &address);
+ found_test =
+ test_map[testcase].Retrieve(target, entry_cstring, &address_test);
+
+ ASSERT_EQ(found, found_test);
+
+ if (found && found_test) {
+ ASSERT_EQ(address, address_test);
+ entry_test = entry_cstring;
+ ASSERT_EQ(entry, entry_test);
+ }
+ }
+
+ void RetrieveTester(int testcase) {
+ int target;
+ target = INT_MIN;
+ CompareRetrieveResult(testcase, target);
+ target = INT_MAX;
+ CompareRetrieveResult(testcase, target);
+
+ srand(time(0));
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ // Retrive (aka, search) for target address and compare results from
+ // AddressMap and StaticAddressMap.
+
+ // First, assign the search target to be one of original testdata that is
+ // known to exist in the map.
+ target = testdata[testcase][data_item];
+ CompareRetrieveResult(testcase, target);
+ // Then, add +2 / -1 bias to target value, in order to test searching for
+ // a target address not stored in the map.
+ target -= 1;
+ CompareRetrieveResult(testcase, target);
+ target += 3;
+ CompareRetrieveResult(testcase, target);
+ // Repeatedly test searching for random target addresses.
+ target = rand();
+ CompareRetrieveResult(testcase, target);
+ }
+ }
+
+ // Test data sets:
+ static const int kNumberTestCases = 4;
+ static const int testsize[];
+ int *testdata[kNumberTestCases];
+
+ AddrMap addr_map[kNumberTestCases];
+ TestMap test_map[kNumberTestCases];
+ char *map_data[kNumberTestCases];
+ google_breakpad::AddressMapSerializer<int, string> serializer;
+};
+
+const int TestStaticAddressMap::testsize[] = {0, 1, 6, 1000};
+
+TEST_F(TestStaticAddressMap, TestEmptyMap) {
+ int testcase = 0;
+ int target;
+ target = INT_MIN;
+ CompareRetrieveResult(testcase, target);
+ target = INT_MAX;
+ CompareRetrieveResult(testcase, target);
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ target = testdata[testcase][data_item];
+ CompareRetrieveResult(testcase, target);
+ target -= 1;
+ CompareRetrieveResult(testcase, target);
+ target += 3;
+ CompareRetrieveResult(testcase, target);
+ target = rand();
+ CompareRetrieveResult(testcase, target);
+ }
+}
+
+TEST_F(TestStaticAddressMap, TestOneElementMap) {
+ int testcase = 1;
+ int target;
+ target = INT_MIN;
+ CompareRetrieveResult(testcase, target);
+ target = INT_MAX;
+ CompareRetrieveResult(testcase, target);
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ target = testdata[testcase][data_item];
+ CompareRetrieveResult(testcase, target);
+ target -= 1;
+ CompareRetrieveResult(testcase, target);
+ target += 3;
+ CompareRetrieveResult(testcase, target);
+ target = rand();
+ CompareRetrieveResult(testcase, target);
+ }
+}
+
+TEST_F(TestStaticAddressMap, TestSixElementsMap) {
+ int testcase = 2;
+ int target;
+ target = INT_MIN;
+ CompareRetrieveResult(testcase, target);
+ target = INT_MAX;
+ CompareRetrieveResult(testcase, target);
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ target = testdata[testcase][data_item];
+ CompareRetrieveResult(testcase, target);
+ target -= 1;
+ CompareRetrieveResult(testcase, target);
+ target += 3;
+ CompareRetrieveResult(testcase, target);
+ target = rand();
+ CompareRetrieveResult(testcase, target);
+ }
+}
+
+TEST_F(TestStaticAddressMap, Test1000RandomElementsMap) {
+ int testcase = 3;
+ int target;
+ target = INT_MIN;
+ CompareRetrieveResult(testcase, target);
+ target = INT_MAX;
+ CompareRetrieveResult(testcase, target);
+ for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
+ target = testdata[testcase][data_item];
+ CompareRetrieveResult(testcase, target);
+ target -= 1;
+ CompareRetrieveResult(testcase, target);
+ target += 3;
+ CompareRetrieveResult(testcase, target);
+ target = rand();
+ CompareRetrieveResult(testcase, target);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map-inl.h
new file mode 100644
index 000000000..777c76218
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map-inl.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_contained_range_map-inl.h: Hierarchically-organized range map,
+// i.e., StaticContainedRangeMap implementation.
+//
+// See static_contained_range_map.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__
+#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__
+
+#include "processor/static_contained_range_map.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+StaticContainedRangeMap<AddressType, EntryType>::StaticContainedRangeMap(
+ const char *base)
+ : base_(*(reinterpret_cast<const AddressType*>(base))),
+ entry_size_(*(reinterpret_cast<const uint32_t*>(base + sizeof(base_)))),
+ entry_ptr_(reinterpret_cast<const EntryType *>(
+ base + sizeof(base_) + sizeof(entry_size_))),
+ map_(base + sizeof(base_) + sizeof(entry_size_) + entry_size_) {
+ if (entry_size_ == 0)
+ entry_ptr_ = NULL;
+}
+
+
+template<typename AddressType, typename EntryType>
+bool StaticContainedRangeMap<AddressType, EntryType>::RetrieveRange(
+ const AddressType &address, const EntryType *&entry) const {
+
+ // Get an iterator to the child range whose high address is equal to or
+ // greater than the supplied address. If the supplied address is higher
+ // than all of the high addresses in the range, then this range does not
+ // contain a child at address, so return false. If the supplied address
+ // is lower than the base address of the child range, then it is not within
+ // the child range, so return false.
+ MapConstIterator iterator = map_.lower_bound(address);
+
+ if (iterator == map_.end())
+ return false;
+
+ const char *memory_child =
+ reinterpret_cast<const char*>(iterator.GetValuePtr());
+
+ StaticContainedRangeMap child_map(memory_child);
+
+ if (address < child_map.base_)
+ return false;
+
+ // The child in iterator->second contains the specified address. Find out
+ // if it has a more-specific descendant that also contains it. If it does,
+ // it will set |entry| appropriately. If not, set |entry| to the child.
+ if (!child_map.RetrieveRange(address, entry))
+ entry = child_map.entry_ptr_;
+
+ return true;
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map.h b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map.h
new file mode 100644
index 000000000..6a9b8b7b6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_contained_range_map.h: StaticContainedRangeMap.
+//
+// StaticContainedRangeMap is similar to ContainedRangeMap. However,
+// StaticContainedRangeMap wraps a StaticMap instead of std::map, and does not
+// support dynamic operations like StoreRange(...).
+// StaticContainedRangeMap provides same RetrieveRange(...) interfaces as
+// ContainedRangeMap.
+//
+// Please see contained_range_map.h for more documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__
+#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__
+
+#include "processor/static_map-inl.h"
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+class StaticContainedRangeMap {
+ public:
+ StaticContainedRangeMap(): base_(), entry_size_(), entry_ptr_(), map_() { }
+ explicit StaticContainedRangeMap(const char *base);
+
+ // Retrieves the most specific (smallest) descendant range encompassing
+ // the specified address. This method will only return entries held by
+ // child ranges, and not the entry contained by |this|. This is necessary
+ // to support a sparsely-populated root range. If no descendant range
+ // encompasses the address, returns false.
+ bool RetrieveRange(const AddressType &address, const EntryType *&entry) const;
+
+ private:
+ friend class ModuleComparer;
+ // AddressToRangeMap stores pointers. This makes reparenting simpler in
+ // StoreRange, because it doesn't need to copy entire objects.
+ typedef StaticContainedRangeMap* SelfPtr;
+ typedef
+ StaticMap<AddressType, StaticContainedRangeMap> AddressToRangeMap;
+ typedef typename AddressToRangeMap::const_iterator MapConstIterator;
+
+ // The base address of this range. The high address does not need to
+ // be stored, because it is used as the key to an object in its parent's
+ // map, and all ContainedRangeMaps except for the root range are contained
+ // within maps. The root range does not actually contain an entry, so its
+ // base_ field is meaningless, and the fact that it has no parent and thus
+ // no key is unimportant. For this reason, the base_ field should only be
+ // is accessed on child ContainedRangeMap objects, and never on |this|.
+ AddressType base_;
+
+ // The entry corresponding to this range. The root range does not
+ // actually contain an entry, so its entry_ field is meaningless. For
+ // this reason, the entry_ field should only be accessed on child
+ // ContainedRangeMap objects, and never on |this|.
+ uint32_t entry_size_;
+ const EntryType *entry_ptr_;
+
+ // The map containing child ranges, keyed by each child range's high
+ // address. This is a pointer to avoid allocating map structures for
+ // leaf nodes, where they are not needed.
+ AddressToRangeMap map_;
+};
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map_unittest.cc
new file mode 100644
index 000000000..4ee47578e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_contained_range_map_unittest.cc
@@ -0,0 +1,320 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_contained_range_map_unittest.cc: Unit tests for
+// StaticContainedRangeMap.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include "breakpad_googletest_includes.h"
+#include "common/scoped_ptr.h"
+#include "processor/contained_range_map-inl.h"
+#include "processor/static_contained_range_map-inl.h"
+#include "processor/simple_serializer-inl.h"
+#include "processor/map_serializers-inl.h"
+#include "processor/logging.h"
+
+namespace {
+
+typedef google_breakpad::ContainedRangeMap<unsigned int, int> CRMMap;
+typedef google_breakpad::StaticContainedRangeMap<unsigned int, int> TestMap;
+
+// Each element in test_data contains the expected result when calling
+// RetrieveRange on an address.
+const int test_data[] = {
+ 0, // 0
+ 0, // 1
+ 0, // 2
+ 0, // 3
+ 0, // 4
+ 0, // 5
+ 0, // 6
+ 0, // 7
+ 9, // 8
+ 7, // 9
+ 1, // 10
+ 5, // 11
+ 6, // 12
+ 6, // 13
+ 6, // 14
+ 6, // 15
+ 6, // 16
+ 6, // 17
+ 6, // 18
+ 5, // 19
+ 7, // 20
+ 8, // 21
+ 0, // 22
+ 0, // 23
+ 0, // 24
+ 0, // 25
+ 0, // 26
+ 0, // 27
+ 0, // 28
+ 0, // 29
+ 10, // 30
+ 10, // 31
+ 10, // 32
+ 11, // 33
+ 11, // 34
+ 11, // 35
+ 0, // 36
+ 0, // 37
+ 0, // 38
+ 0, // 39
+ 14, // 40
+ 14, // 41
+ 14, // 42
+ 14, // 43
+ 15, // 44
+ 15, // 45
+ 15, // 46
+ 15, // 47
+ 0, // 48
+ 0, // 49
+ 19, // 50
+ 18, // 51
+ 18, // 52
+ 18, // 53
+ 18, // 54
+ 18, // 55
+ 18, // 56
+ 18, // 57
+ 18, // 58
+ 20, // 59
+ 21, // 60
+ 25, // 61
+ 26, // 62
+ 26, // 63
+ 26, // 64
+ 26, // 65
+ 26, // 66
+ 26, // 67
+ 24, // 68
+ 22, // 69
+ 30, // 70
+ 30, // 71
+ 30, // 72
+ 30, // 73
+ 31, // 74
+ 31, // 75
+ 30, // 76
+ 32, // 77
+ 32, // 78
+ 30, // 79
+ 34, // 80
+ 35, // 81
+ 36, // 82
+ 39, // 83
+ 38, // 84
+ 37, // 85
+ 43, // 86
+ 44, // 87
+ 41, // 88
+ 45, // 89
+ 42, // 90
+ 0, // 91
+ 0, // 92
+ 0, // 93
+ 0, // 94
+ 0, // 95
+ 0, // 96
+ 0, // 97
+ 0, // 98
+ 0 // 99
+};
+
+} // namespace
+
+namespace google_breakpad {
+
+class TestStaticCRMMap : public ::testing::Test {
+ protected:
+ void SetUp();
+
+ // A referrence map for testing StaticCRMMap.
+ google_breakpad::ContainedRangeMap<unsigned int, int> crm_map_;
+
+ // Static version of crm_map using serialized data of crm_map.
+ // The goal of testing is to make sure TestMap provides same results for
+ // lookup operation(s) as CRMMap does.
+ google_breakpad::StaticContainedRangeMap<unsigned int, int> test_map_;
+
+ google_breakpad::ContainedRangeMapSerializer<unsigned int, int> serializer_;
+
+ scoped_array<char> serialized_data_;
+};
+
+void TestStaticCRMMap::SetUp() {
+ // First, do the StoreRange tests. This validates the containment
+ // rules.
+ // We confirm the referrence map correctly stores data during setup.
+ ASSERT_TRUE (crm_map_.StoreRange(10, 10, 1));
+ ASSERT_FALSE(crm_map_.StoreRange(10, 10, 2)); // exactly equal to 1
+ ASSERT_FALSE(crm_map_.StoreRange(11, 10, 3)); // begins inside 1 and extends up
+ ASSERT_FALSE(crm_map_.StoreRange( 9, 10, 4)); // begins below 1 and ends inside
+ ASSERT_TRUE (crm_map_.StoreRange(11, 9, 5)); // contained by existing
+ ASSERT_TRUE (crm_map_.StoreRange(12, 7, 6));
+ ASSERT_TRUE (crm_map_.StoreRange( 9, 12, 7)); // contains existing
+ ASSERT_TRUE (crm_map_.StoreRange( 9, 13, 8));
+ ASSERT_TRUE (crm_map_.StoreRange( 8, 14, 9));
+ ASSERT_TRUE (crm_map_.StoreRange(30, 3, 10));
+ ASSERT_TRUE (crm_map_.StoreRange(33, 3, 11));
+ ASSERT_TRUE (crm_map_.StoreRange(30, 6, 12)); // storable but totally masked
+ ASSERT_TRUE (crm_map_.StoreRange(40, 8, 13)); // will be totally masked
+ ASSERT_TRUE (crm_map_.StoreRange(40, 4, 14));
+ ASSERT_TRUE (crm_map_.StoreRange(44, 4, 15));
+ ASSERT_FALSE(crm_map_.StoreRange(32, 10, 16)); // begins in #10, ends in #14
+ ASSERT_FALSE(crm_map_.StoreRange(50, 0, 17)); // zero length
+ ASSERT_TRUE (crm_map_.StoreRange(50, 10, 18));
+ ASSERT_TRUE (crm_map_.StoreRange(50, 1, 19));
+ ASSERT_TRUE (crm_map_.StoreRange(59, 1, 20));
+ ASSERT_TRUE (crm_map_.StoreRange(60, 1, 21));
+ ASSERT_TRUE (crm_map_.StoreRange(69, 1, 22));
+ ASSERT_TRUE (crm_map_.StoreRange(60, 10, 23));
+ ASSERT_TRUE (crm_map_.StoreRange(68, 1, 24));
+ ASSERT_TRUE (crm_map_.StoreRange(61, 1, 25));
+ ASSERT_TRUE (crm_map_.StoreRange(61, 8, 26));
+ ASSERT_FALSE(crm_map_.StoreRange(59, 9, 27));
+ ASSERT_FALSE(crm_map_.StoreRange(59, 10, 28));
+ ASSERT_FALSE(crm_map_.StoreRange(59, 11, 29));
+ ASSERT_TRUE (crm_map_.StoreRange(70, 10, 30));
+ ASSERT_TRUE (crm_map_.StoreRange(74, 2, 31));
+ ASSERT_TRUE (crm_map_.StoreRange(77, 2, 32));
+ ASSERT_FALSE(crm_map_.StoreRange(72, 6, 33));
+ ASSERT_TRUE (crm_map_.StoreRange(80, 3, 34));
+ ASSERT_TRUE (crm_map_.StoreRange(81, 1, 35));
+ ASSERT_TRUE (crm_map_.StoreRange(82, 1, 36));
+ ASSERT_TRUE (crm_map_.StoreRange(83, 3, 37));
+ ASSERT_TRUE (crm_map_.StoreRange(84, 1, 38));
+ ASSERT_TRUE (crm_map_.StoreRange(83, 1, 39));
+ ASSERT_TRUE (crm_map_.StoreRange(86, 5, 40));
+ ASSERT_TRUE (crm_map_.StoreRange(88, 1, 41));
+ ASSERT_TRUE (crm_map_.StoreRange(90, 1, 42));
+ ASSERT_TRUE (crm_map_.StoreRange(86, 1, 43));
+ ASSERT_TRUE (crm_map_.StoreRange(87, 1, 44));
+ ASSERT_TRUE (crm_map_.StoreRange(89, 1, 45));
+ ASSERT_TRUE (crm_map_.StoreRange(87, 4, 46));
+ ASSERT_TRUE (crm_map_.StoreRange(87, 3, 47));
+ ASSERT_FALSE(crm_map_.StoreRange(86, 2, 48));
+
+ // Serialize crm_map to generate serialized data.
+ unsigned int size;
+ serialized_data_.reset(serializer_.Serialize(&crm_map_, &size));
+ BPLOG(INFO) << "Serialized data size: " << size << " Bytes.";
+
+ // Construct test_map_ from serialized data.
+ test_map_ = TestMap(serialized_data_.get());
+}
+
+TEST_F(TestStaticCRMMap, TestEmptyMap) {
+ CRMMap empty_crm_map;
+
+ unsigned int size;
+ scoped_array<char> serialized_data;
+ serialized_data.reset(serializer_.Serialize(&empty_crm_map, &size));
+ scoped_ptr<TestMap> test_map(new TestMap(serialized_data.get()));
+
+ const unsigned int kCorrectSizeForEmptyMap = 16;
+ ASSERT_EQ(kCorrectSizeForEmptyMap, size);
+
+ const int *entry_test;
+ ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test));
+ ASSERT_FALSE(test_map->RetrieveRange(0, entry_test));
+ ASSERT_FALSE(test_map->RetrieveRange(10, entry_test));
+}
+
+TEST_F(TestStaticCRMMap, TestSingleElementMap) {
+ CRMMap crm_map;
+ // Test on one element:
+ int entry = 1;
+ crm_map.StoreRange(10, 10, entry);
+
+ unsigned int size;
+ scoped_array<char> serialized_data;
+ serialized_data.reset(serializer_.Serialize(&crm_map, &size));
+ scoped_ptr<TestMap> test_map(new TestMap(serialized_data.get()));
+
+ const unsigned int kCorrectSizeForSingleElementMap = 40;
+ ASSERT_EQ(kCorrectSizeForSingleElementMap, size);
+
+ const int *entry_test;
+ ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test));
+ ASSERT_FALSE(test_map->RetrieveRange(0, entry_test));
+ ASSERT_TRUE(test_map->RetrieveRange(10, entry_test));
+ ASSERT_EQ(*entry_test, entry);
+ ASSERT_TRUE(test_map->RetrieveRange(13, entry_test));
+ ASSERT_EQ(*entry_test, entry);
+}
+
+TEST_F(TestStaticCRMMap, RunTestData) {
+ unsigned int test_high = sizeof(test_data) / sizeof(test_data[0]);
+
+ // Now, do the RetrieveRange tests. This further validates that the
+ // objects were stored properly and that retrieval returns the correct
+ // object.
+ // If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a
+ // new test_data array will be printed. Exercise caution when doing this.
+ // Be sure to verify the results manually!
+#ifdef GENERATE_TEST_DATA
+ printf(" const int test_data[] = {\n");
+#endif // GENERATE_TEST_DATA
+
+ for (unsigned int address = 0; address < test_high; ++address) {
+ const int *entryptr;
+ int value = 0;
+ if (test_map_.RetrieveRange(address, entryptr))
+ value = *entryptr;
+
+#ifndef GENERATE_TEST_DATA
+ // Don't use ASSERT inside the loop because it won't show the failed
+ // |address|, and the line number will always be the same. That makes
+ // it difficult to figure out which test failed.
+ EXPECT_EQ(value, test_data[address]) << "FAIL: retrieve address "
+ << address;
+#else // !GENERATE_TEST_DATA
+ printf(" %d%c%s // %d\n", value,
+ address == test_high - 1 ? ' ' : ',',
+ value < 10 ? " " : "",
+ address);
+#endif // !GENERATE_TEST_DATA
+ }
+
+#ifdef GENERATE_TEST_DATA
+ printf(" };\n");
+#endif // GENERATE_TEST_DATA
+}
+
+} // namespace google_breakpad
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/static_map-inl.h
new file mode 100644
index 000000000..e6aac6aba
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_map-inl.h
@@ -0,0 +1,176 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_map-inl.h: StaticMap implementation.
+//
+// See static_map.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+
+#ifndef PROCESSOR_STATIC_MAP_INL_H__
+#define PROCESSOR_STATIC_MAP_INL_H__
+
+#include "processor/static_map.h"
+#include "processor/static_map_iterator-inl.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename Key, typename Value, typename Compare>
+StaticMap<Key, Value, Compare>::StaticMap(const char* raw_data)
+ : raw_data_(raw_data),
+ compare_() {
+ // First 4 Bytes store the number of nodes.
+ num_nodes_ = *(reinterpret_cast<const uint32_t*>(raw_data_));
+
+ offsets_ = reinterpret_cast<const uint32_t*>(
+ raw_data_ + sizeof(num_nodes_));
+
+ keys_ = reinterpret_cast<const Key*>(
+ raw_data_ + (1 + num_nodes_) * sizeof(uint32_t));
+}
+
+// find(), lower_bound() and upper_bound() implement binary search algorithm.
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>
+StaticMap<Key, Value, Compare>::find(const Key &key) const {
+ int begin = 0;
+ int end = num_nodes_;
+ int middle;
+ int compare_result;
+ while (begin < end) {
+ middle = begin + (end - begin) / 2;
+ compare_result = compare_(key, GetKeyAtIndex(middle));
+ if (compare_result == 0)
+ return IteratorAtIndex(middle);
+ if (compare_result < 0) {
+ end = middle;
+ } else {
+ begin = middle + 1;
+ }
+ }
+ return this->end();
+}
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>
+StaticMap<Key, Value, Compare>::lower_bound(const Key &key) const {
+ int begin = 0;
+ int end = num_nodes_;
+ int middle;
+ int comp_result;
+ while (begin < end) {
+ middle = begin + (end - begin) / 2;
+ comp_result = compare_(key, GetKeyAtIndex(middle));
+ if (comp_result == 0)
+ return IteratorAtIndex(middle);
+ if (comp_result < 0) {
+ end = middle;
+ } else {
+ begin = middle + 1;
+ }
+ }
+ return IteratorAtIndex(begin);
+}
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>
+StaticMap<Key, Value, Compare>::upper_bound(const Key &key) const {
+ int begin = 0;
+ int end = num_nodes_;
+ int middle;
+ int compare_result;
+ while (begin < end) {
+ middle = begin + (end - begin) / 2;
+ compare_result = compare_(key, GetKeyAtIndex(middle));
+ if (compare_result == 0)
+ return IteratorAtIndex(middle + 1);
+ if (compare_result < 0) {
+ end = middle;
+ } else {
+ begin = middle + 1;
+ }
+ }
+ return IteratorAtIndex(begin);
+}
+
+template<typename Key, typename Value, typename Compare>
+bool StaticMap<Key, Value, Compare>::ValidateInMemoryStructure() const {
+ // check the number of nodes is non-negative:
+ if (!raw_data_) return false;
+ int32_t num_nodes = *(reinterpret_cast<const int32_t*>(raw_data_));
+ if (num_nodes < 0) {
+ BPLOG(INFO) << "StaticMap check failed: negative number of nodes";
+ return false;
+ }
+
+ int node_index = 0;
+ if (num_nodes_) {
+ uint64_t first_offset = sizeof(int32_t) * (num_nodes_ + 1)
+ + sizeof(Key) * num_nodes_;
+ // Num_nodes_ is too large.
+ if (first_offset > 0xffffffffUL) {
+ BPLOG(INFO) << "StaticMap check failed: size exceeds limit";
+ return false;
+ }
+ if (offsets_[node_index] != static_cast<uint32_t>(first_offset)) {
+ BPLOG(INFO) << "StaticMap check failed: first node offset is incorrect";
+ return false;
+ }
+ }
+
+ for (node_index = 1; node_index < num_nodes_; ++node_index) {
+ // Check offsets[i] is strictly increasing:
+ if (offsets_[node_index] <= offsets_[node_index - 1]) {
+ BPLOG(INFO) << "StaticMap check failed: node offsets non-increasing";
+ return false;
+ }
+ // Check Key[i] is strictly increasing as no duplicate keys are allowed.
+ if (compare_(GetKeyAtIndex(node_index),
+ GetKeyAtIndex(node_index - 1)) <= 0) {
+ BPLOG(INFO) << "StaticMap check failed: node keys non-increasing";
+ return false;
+ }
+ }
+ return true;
+}
+
+template<typename Key, typename Value, typename Compare>
+const Key StaticMap<Key, Value, Compare>::GetKeyAtIndex(int index) const {
+ if (index < 0 || index >= num_nodes_) {
+ BPLOG(ERROR) << "Key index out of range error";
+ // Key type is required to be primitive type. Return 0 if index is invalid.
+ return 0;
+ }
+ return keys_[index];
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_map.h b/toolkit/crashreporter/google-breakpad/src/processor/static_map.h
new file mode 100644
index 000000000..9723ab2a8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_map.h
@@ -0,0 +1,144 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_map.h: StaticMap.
+//
+// StaticMap provides lookup interfaces and iterators similar as stl::map's.
+// These lookup operations are purely Read-Only, thus memory
+// allocation & deallocation is mostly avoided (intentionally).
+//
+// The chunk of memory should contain data with pre-defined pattern:
+// **************** header ***************
+// uint32 (4 bytes): number of nodes
+// uint32 (4 bytes): address offset of node1's mapped_value
+// uint32 (4 bytes): address offset of node2's mapped_value
+// ...
+// uint32 (4 bytes): address offset of nodeN's mapped_value
+//
+// ************* Key array ************
+// (X bytes): node1's key
+// (X bytes): node2's key
+// ...
+// (X bytes): nodeN's key
+//
+// ************* Value array **********
+// (? bytes): node1's mapped_value
+// (? bytes): node2's mapped_value
+// ...
+// (? bytes): nodeN's mapped_value
+//
+// REQUIREMENT: Key type MUST be primitive type or pointers so that:
+// X = sizeof(typename Key);
+//
+// Note: since address offset is stored as uint32, user should keep in mind that
+// StaticMap only supports up to 4GB size of memory data.
+
+// Author: Siyang Xie (lambxsy@google.com)
+
+
+#ifndef PROCESSOR_STATIC_MAP_H__
+#define PROCESSOR_STATIC_MAP_H__
+
+#include "processor/static_map_iterator-inl.h"
+
+namespace google_breakpad {
+
+// Default functor to compare keys.
+template<typename Key>
+class DefaultCompare {
+ public:
+ int operator()(const Key &k1, const Key &k2) const {
+ if (k1 < k2) return -1;
+ if (k1 == k2) return 0;
+ return 1;
+ }
+};
+
+template<typename Key, typename Value, typename Compare = DefaultCompare<Key> >
+class StaticMap {
+ public:
+ typedef StaticMapIterator<Key, Value, Compare> iterator;
+ typedef StaticMapIterator<Key, Value, Compare> const_iterator;
+
+ StaticMap() : raw_data_(0),
+ num_nodes_(0),
+ offsets_(0),
+ compare_() { }
+
+ explicit StaticMap(const char* raw_data);
+
+ inline bool empty() const { return num_nodes_ == 0; }
+ inline unsigned int size() const { return num_nodes_; }
+
+ // Return iterators.
+ inline iterator begin() const { return IteratorAtIndex(0); }
+ inline iterator last() const { return IteratorAtIndex(num_nodes_ - 1); }
+ inline iterator end() const { return IteratorAtIndex(num_nodes_); }
+ inline iterator IteratorAtIndex(int index) const {
+ return iterator(raw_data_, index);
+ }
+
+ // Lookup operations.
+ iterator find(const Key &k) const;
+
+ // lower_bound(k) searches in a sorted range for the first element that has a
+ // key not less than the argument k.
+ iterator lower_bound(const Key &k) const;
+
+ // upper_bound(k) searches in a sorted range for the first element that has a
+ // key greater than the argument k.
+ iterator upper_bound(const Key &k) const;
+
+ // Checks if the underlying memory data conforms to the predefined pattern:
+ // first check the number of nodes is non-negative,
+ // then check both offsets and keys are strictly increasing (sorted).
+ bool ValidateInMemoryStructure() const;
+
+ private:
+ const Key GetKeyAtIndex(int i) const;
+
+ // Start address of a raw memory chunk with serialized data.
+ const char* raw_data_;
+
+ // Number of nodes in the static map.
+ int32_t num_nodes_;
+
+ // Array of offset addresses for stored values.
+ // For example:
+ // address_of_i-th_node_value = raw_data_ + offsets_[i]
+ const uint32_t* offsets_;
+
+ // keys_[i] = key of i_th node
+ const Key* keys_;
+
+ Compare compare_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator-inl.h
new file mode 100644
index 000000000..7a7db5ad9
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator-inl.h
@@ -0,0 +1,147 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_map_iterator-inl.h: StaticMapIterator implementation.
+//
+// See static_map_iterator.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_MAP_ITERATOR_INL_H__
+#define PROCESSOR_STATIC_MAP_ITERATOR_INL_H__
+
+#include "processor/static_map_iterator.h"
+
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>::StaticMapIterator(const char* base,
+ const int &index):
+ index_(index), base_(base) {
+ // See static_map.h for documentation on
+ // bytes format of serialized StaticMap data.
+ num_nodes_ = *(reinterpret_cast<const int32_t*>(base_));
+ offsets_ = reinterpret_cast<const uint32_t*>(base_ + sizeof(num_nodes_));
+ keys_ = reinterpret_cast<const Key*>(
+ base_ + (1 + num_nodes_) * sizeof(num_nodes_));
+}
+
+// Increment & Decrement operators:
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>&
+StaticMapIterator<Key, Value, Compare>::operator++() {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "operator++ on invalid iterator";
+ return *this;
+ }
+ if (++index_ > num_nodes_) index_ = num_nodes_;
+ return *this;
+}
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>
+StaticMapIterator<Key, Value, Compare>::operator++(int postfix_operator) {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "operator++ on invalid iterator";
+ return *this;
+ }
+ StaticMapIterator<Key, Value, Compare> tmp = *this;
+ if (++index_ > num_nodes_) index_ = num_nodes_;
+ return tmp;
+}
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>&
+StaticMapIterator<Key, Value, Compare>::operator--() {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "operator++ on invalid iterator";
+ return *this;
+ }
+
+ if (--index_ < 0) index_ = 0;
+ return *this;
+}
+
+template<typename Key, typename Value, typename Compare>
+StaticMapIterator<Key, Value, Compare>
+StaticMapIterator<Key, Value, Compare>::operator--(int postfix_operator) {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "operator++ on invalid iterator";
+ return *this;
+ }
+ StaticMapIterator<Key, Value, Compare> tmp = *this;
+
+ if (--index_ < 0) index_ = 0;
+ return tmp;
+}
+
+template<typename Key, typename Value, typename Compare>
+const Key* StaticMapIterator<Key, Value, Compare>::GetKeyPtr() const {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "call GetKeyPtr() on invalid iterator";
+ return NULL;
+ }
+ return &(keys_[index_]);
+}
+
+template<typename Key, typename Value, typename Compare>
+const char* StaticMapIterator<Key, Value, Compare>::GetValueRawPtr() const {
+ if (!IsValid()) {
+ BPLOG(ERROR) << "call GetValuePtr() on invalid iterator";
+ return NULL;
+ }
+ return base_ + offsets_[index_];
+}
+
+template<typename Key, typename Value, typename Compare>
+bool StaticMapIterator<Key, Value, Compare>::operator==(
+ const StaticMapIterator<Key, Value, Compare>& x) const {
+ return base_ == x.base_ && index_ == x.index_;
+}
+
+template<typename Key, typename Value, typename Compare>
+bool StaticMapIterator<Key, Value, Compare>::operator!=(
+ const StaticMapIterator<Key, Value, Compare>& x) const {
+ // Only need to compare base_ and index_.
+ // Other data members are auxiliary.
+ return base_ != x.base_ || index_ != x.index_;
+}
+
+template<typename Key, typename Value, typename Compare>
+bool StaticMapIterator<Key, Value, Compare>::IsValid() const {
+ if (!base_ || index_ < 0 || index_ > num_nodes_)
+ return false;
+
+ return true;
+}
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_MAP_ITERATOR_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator.h b/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator.h
new file mode 100644
index 000000000..1af8fff45
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_map_iterator.h
@@ -0,0 +1,112 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_map_iterator.h: StaticMapIterator template class declaration.
+//
+// StaticMapIterator provides increment and decrement operators to iterate
+// through a StaticMap map. It does not provide *, -> operators, user should
+// use GetKeyPtr(), GetKey(), GetValuePtr() interfaces to retrieve data or
+// pointer to data. StaticMapIterator is essentially a const_iterator.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+
+#ifndef PROCESSOR_STATIC_MAP_ITERATOR_H__
+#define PROCESSOR_STATIC_MAP_ITERATOR_H__
+
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+// Forward declaration.
+template<typename Key, typename Value, typename Compare> class StaticMap;
+
+// StaticMapIterator does not support operator*() or operator->(),
+// User should use GetKey(), GetKeyPtr(), GetValuePtr() instead;
+template<typename Key, typename Value, typename Compare>
+class StaticMapIterator {
+ public:
+ // Constructors.
+ StaticMapIterator(): index_(-1), base_(NULL) { }
+
+ // Increment & Decrement operators:
+ StaticMapIterator& operator++();
+ StaticMapIterator operator++(int post_fix_operator);
+
+ StaticMapIterator& operator--();
+ StaticMapIterator operator--(int post_fix_operator);
+
+ // Interface for retrieving data / pointer to data.
+ const Key* GetKeyPtr() const;
+
+ // Run time error will occur if GetKey() is called on an invalid iterator.
+ inline const Key GetKey() const { return *GetKeyPtr(); }
+
+ // return a raw memory pointer that points to the start address of value.
+ const char* GetValueRawPtr() const;
+
+ // return a reinterpret-casted pointer to the value.
+ inline const Value* GetValuePtr() const {
+ return reinterpret_cast<const Value*>(GetValueRawPtr());
+ }
+
+ bool operator==(const StaticMapIterator& x) const;
+ bool operator!=(const StaticMapIterator& x) const;
+
+ // Check if this iterator is valid.
+ // If iterator is invalid, user is forbidden to use ++/-- operator
+ // or interfaces for retrieving data / pointer to data.
+ bool IsValid() const;
+
+ private:
+ friend class StaticMap<Key, Value, Compare>;
+
+ // Only StaticMap can call this constructor.
+ explicit StaticMapIterator(const char* base, const int32_t &index);
+
+ // Index of node that the iterator is pointing to.
+ int32_t index_;
+
+ // Beginning address of the serialized map data.
+ const char* base_;
+
+ // Number of nodes in the map. Use it to identify end() iterator.
+ int32_t num_nodes_;
+
+ // offsets_ is an array of offset addresses of mapped values.
+ // For example:
+ // address_of_i-th_node_value = base_ + offsets_[i]
+ const uint32_t* offsets_;
+
+ // keys_[i] = key of i_th node.
+ const Key* keys_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_MAP_ITERATOR_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/static_map_unittest.cc
new file mode 100644
index 000000000..393d43d5c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_map_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_map_unittest.cc: Unit tests for StaticMap.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include <climits>
+#include <map>
+
+#include "breakpad_googletest_includes.h"
+#include "processor/static_map-inl.h"
+
+
+typedef int ValueType;
+typedef int KeyType;
+typedef google_breakpad::StaticMap< KeyType, ValueType > TestMap;
+typedef std::map< KeyType, ValueType > StdMap;
+
+template<typename Key, typename Value>
+class SimpleMapSerializer {
+ public:
+ static char* Serialize(const std::map<Key, Value> &stdmap,
+ unsigned int* size = NULL) {
+ unsigned int size_per_node =
+ sizeof(uint32_t) + sizeof(Key) + sizeof(Value);
+ unsigned int memsize = sizeof(int32_t) + size_per_node * stdmap.size();
+ if (size) *size = memsize;
+
+ // Allocate memory for serialized data:
+ char* mem = reinterpret_cast<char*>(operator new(memsize));
+ char* address = mem;
+
+ // Writer the number of nodes:
+ new (address) uint32_t(static_cast<uint32_t>(stdmap.size()));
+ address += sizeof(uint32_t);
+
+ // Nodes' offset:
+ uint32_t* offsets = reinterpret_cast<uint32_t*>(address);
+ address += sizeof(uint32_t) * stdmap.size();
+
+ // Keys:
+ Key* keys = reinterpret_cast<Key*>(address);
+ address += sizeof(Key) * stdmap.size();
+
+ // Traversing map:
+ typename std::map<Key, Value>::const_iterator iter = stdmap.begin();
+ for (int index = 0; iter != stdmap.end(); ++iter, ++index) {
+ offsets[index] = static_cast<unsigned int>(address - mem);
+ keys[index] = iter->first;
+ new (address) Value(iter->second);
+ address += sizeof(Value);
+ }
+ return mem;
+ }
+};
+
+
+class TestInvalidMap : public ::testing::Test {
+ protected:
+ void SetUp() {
+ memset(data, 0, kMemorySize);
+ }
+
+ // 40 Bytes memory can hold a StaticMap with up to 3 nodes.
+ static const int kMemorySize = 40;
+ char data[kMemorySize];
+ TestMap test_map;
+};
+
+TEST_F(TestInvalidMap, TestNegativeNumberNodes) {
+ memset(data, 0xff, sizeof(uint32_t)); // Set the number of nodes = -1
+ test_map = TestMap(data);
+ ASSERT_FALSE(test_map.ValidateInMemoryStructure());
+}
+
+TEST_F(TestInvalidMap, TestWrongOffsets) {
+ uint32_t* header = reinterpret_cast<uint32_t*>(data);
+ const uint32_t kNumNodes = 2;
+ const uint32_t kHeaderOffset =
+ sizeof(uint32_t) + kNumNodes * (sizeof(uint32_t) + sizeof(KeyType));
+
+ header[0] = kNumNodes;
+ header[1] = kHeaderOffset + 3; // Wrong offset for first node
+ test_map = TestMap(data);
+ ASSERT_FALSE(test_map.ValidateInMemoryStructure());
+
+ header[1] = kHeaderOffset; // Correct offset for first node
+ header[2] = kHeaderOffset - 1; // Wrong offset for second node
+ test_map = TestMap(data);
+ ASSERT_FALSE(test_map.ValidateInMemoryStructure());
+}
+
+TEST_F(TestInvalidMap, TestUnSortedKeys) {
+ uint32_t* header = reinterpret_cast<uint32_t*>(data);
+ const uint32_t kNumNodes = 2;
+ const uint32_t kHeaderOffset =
+ sizeof(uint32_t) + kNumNodes * (sizeof(uint32_t) + sizeof(KeyType));
+ header[0] = kNumNodes;
+ header[1] = kHeaderOffset;
+ header[2] = kHeaderOffset + sizeof(ValueType);
+
+ KeyType* keys = reinterpret_cast<KeyType*>(
+ data + (kNumNodes + 1) * sizeof(uint32_t));
+ // Set keys in non-increasing order.
+ keys[0] = 10;
+ keys[1] = 7;
+ test_map = TestMap(data);
+ ASSERT_FALSE(test_map.ValidateInMemoryStructure());
+}
+
+
+class TestValidMap : public ::testing::Test {
+ protected:
+ void SetUp() {
+ int testcase = 0;
+
+ // Empty map.
+ map_data[testcase] =
+ serializer.Serialize(std_map[testcase], &size[testcase]);
+ test_map[testcase] = TestMap(map_data[testcase]);
+ ++testcase;
+
+ // Single element.
+ std_map[testcase].insert(std::make_pair(2, 8));
+ map_data[testcase] =
+ serializer.Serialize(std_map[testcase], &size[testcase]);
+ test_map[testcase] = TestMap(map_data[testcase]);
+ ++testcase;
+
+ // 100 elements.
+ for (int i = 0; i < 100; ++i)
+ std_map[testcase].insert(std::make_pair(i, 2 * i));
+ map_data[testcase] =
+ serializer.Serialize(std_map[testcase], &size[testcase]);
+ test_map[testcase] = TestMap(map_data[testcase]);
+ ++testcase;
+
+ // 1000 random elements.
+ for (int i = 0; i < 1000; ++i)
+ std_map[testcase].insert(std::make_pair(rand(), rand()));
+ map_data[testcase] =
+ serializer.Serialize(std_map[testcase], &size[testcase]);
+ test_map[testcase] = TestMap(map_data[testcase]);
+
+ // Set correct size of memory allocation for each test case.
+ unsigned int size_per_node =
+ sizeof(uint32_t) + sizeof(KeyType) + sizeof(ValueType);
+ for (testcase = 0; testcase < kNumberTestCases; ++testcase) {
+ correct_size[testcase] =
+ sizeof(uint32_t) + std_map[testcase].size() * size_per_node;
+ }
+ }
+
+ void TearDown() {
+ for (int i = 0;i < kNumberTestCases; ++i)
+ ::operator delete(map_data[i]);
+ }
+
+
+ void IteratorTester(int test_case) {
+ // scan through:
+ iter_test = test_map[test_case].begin();
+ iter_std = std_map[test_case].begin();
+
+ for (; iter_test != test_map[test_case].end() &&
+ iter_std != std_map[test_case].end();
+ ++iter_test, ++iter_std) {
+ ASSERT_EQ(iter_test.GetKey(), iter_std->first);
+ ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
+ }
+ ASSERT_TRUE(iter_test == test_map[test_case].end()
+ && iter_std == std_map[test_case].end());
+
+ // Boundary testcase.
+ if (!std_map[test_case].empty()) {
+ // rear boundary case:
+ iter_test = test_map[test_case].end();
+ iter_std = std_map[test_case].end();
+ --iter_std;
+ --iter_test;
+ ASSERT_EQ(iter_test.GetKey(), iter_std->first);
+ ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
+
+ ++iter_test;
+ ++iter_std;
+ ASSERT_TRUE(iter_test == test_map[test_case].end());
+
+ --iter_test;
+ --iter_std;
+ ASSERT_TRUE(iter_test != test_map[test_case].end());
+ ASSERT_TRUE(iter_test == test_map[test_case].last());
+ ASSERT_EQ(iter_test.GetKey(), iter_std->first);
+ ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
+
+ // front boundary case:
+ iter_test = test_map[test_case].begin();
+ --iter_test;
+ ASSERT_TRUE(iter_test == test_map[test_case].begin());
+ }
+ }
+
+ void CompareLookupResult(int test_case) {
+ bool found1 = (iter_test != test_map[test_case].end());
+ bool found2 = (iter_std != std_map[test_case].end());
+ ASSERT_EQ(found1, found2);
+
+ if (found1 && found2) {
+ ASSERT_EQ(iter_test.GetKey(), iter_std->first);
+ ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
+ }
+ }
+
+ void FindTester(int test_case, const KeyType &key) {
+ iter_test = test_map[test_case].find(key);
+ iter_std = std_map[test_case].find(key);
+ CompareLookupResult(test_case);
+ }
+
+ void LowerBoundTester(int test_case, const KeyType &key) {
+ iter_test = test_map[test_case].lower_bound(key);
+ iter_std = std_map[test_case].lower_bound(key);
+ CompareLookupResult(test_case);
+ }
+
+ void UpperBoundTester(int test_case, const KeyType &key) {
+ iter_test = test_map[test_case].upper_bound(key);
+ iter_std = std_map[test_case].upper_bound(key);
+ CompareLookupResult(test_case);
+ }
+
+ void LookupTester(int test_case) {
+ StdMap::const_iterator iter;
+ // Test find():
+ for (iter = std_map[test_case].begin();
+ iter != std_map[test_case].end();
+ ++iter) {
+ FindTester(test_case, iter->first);
+ FindTester(test_case, iter->first + 1);
+ FindTester(test_case, iter->first - 1);
+ }
+ FindTester(test_case, INT_MIN);
+ FindTester(test_case, INT_MAX);
+ // random test:
+ for (int i = 0; i < rand()%5000 + 5000; ++i)
+ FindTester(test_case, rand());
+
+ // Test lower_bound():
+ for (iter = std_map[test_case].begin();
+ iter != std_map[test_case].end();
+ ++iter) {
+ LowerBoundTester(test_case, iter->first);
+ LowerBoundTester(test_case, iter->first + 1);
+ LowerBoundTester(test_case, iter->first - 1);
+ }
+ LowerBoundTester(test_case, INT_MIN);
+ LowerBoundTester(test_case, INT_MAX);
+ // random test:
+ for (int i = 0; i < rand()%5000 + 5000; ++i)
+ LowerBoundTester(test_case, rand());
+
+ // Test upper_bound():
+ for (iter = std_map[test_case].begin();
+ iter != std_map[test_case].end();
+ ++iter) {
+ UpperBoundTester(test_case, iter->first);
+ UpperBoundTester(test_case, iter->first + 1);
+ UpperBoundTester(test_case, iter->first - 1);
+ }
+ UpperBoundTester(test_case, INT_MIN);
+ UpperBoundTester(test_case, INT_MAX);
+ // random test:
+ for (int i = 0; i < rand()%5000 + 5000; ++i)
+ UpperBoundTester(test_case, rand());
+ }
+
+ static const int kNumberTestCases = 4;
+ StdMap std_map[kNumberTestCases];
+ TestMap test_map[kNumberTestCases];
+ TestMap::const_iterator iter_test;
+ StdMap::const_iterator iter_std;
+ char* map_data[kNumberTestCases];
+ unsigned int size[kNumberTestCases];
+ unsigned int correct_size[kNumberTestCases];
+ SimpleMapSerializer<KeyType, ValueType> serializer;
+};
+
+TEST_F(TestValidMap, TestEmptyMap) {
+ int test_case = 0;
+ // Assert memory size allocated during serialization is correct.
+ ASSERT_EQ(correct_size[test_case], size[test_case]);
+
+ // Sanity check of serialized data:
+ ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
+ ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
+ ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
+
+ // Test Iterator.
+ IteratorTester(test_case);
+
+ // Test lookup operations.
+ LookupTester(test_case);
+}
+
+TEST_F(TestValidMap, TestSingleElement) {
+ int test_case = 1;
+ // Assert memory size allocated during serialization is correct.
+ ASSERT_EQ(correct_size[test_case], size[test_case]);
+
+ // Sanity check of serialized data:
+ ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
+ ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
+ ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
+
+ // Test Iterator.
+ IteratorTester(test_case);
+
+ // Test lookup operations.
+ LookupTester(test_case);
+}
+
+TEST_F(TestValidMap, Test100Elements) {
+ int test_case = 2;
+ // Assert memory size allocated during serialization is correct.
+ ASSERT_EQ(correct_size[test_case], size[test_case]);
+
+ // Sanity check of serialized data:
+ ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
+ ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
+ ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
+
+ // Test Iterator.
+ IteratorTester(test_case);
+
+ // Test lookup operations.
+ LookupTester(test_case);
+}
+
+TEST_F(TestValidMap, Test1000RandomElements) {
+ int test_case = 3;
+ // Assert memory size allocated during serialization is correct.
+ ASSERT_EQ(correct_size[test_case], size[test_case]);
+
+ // Sanity check of serialized data:
+ ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
+ ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
+ ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
+
+ // Test Iterator.
+ IteratorTester(test_case);
+
+ // Test lookup operations.
+ LookupTester(test_case);
+}
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_range_map-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map-inl.h
new file mode 100644
index 000000000..f6cef1a9e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map-inl.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_range_map-inl.h: StaticRangeMap implementation.
+//
+// See static_range_map.h for documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_RANGE_MAP_INL_H__
+#define PROCESSOR_STATIC_RANGE_MAP_INL_H__
+
+#include "processor/static_range_map.h"
+#include "processor/logging.h"
+
+namespace google_breakpad {
+
+template<typename AddressType, typename EntryType>
+bool StaticRangeMap<AddressType, EntryType>::RetrieveRange(
+ const AddressType &address, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size) const {
+ MapConstIterator iterator = map_.lower_bound(address);
+ if (iterator == map_.end())
+ return false;
+
+ // The map is keyed by the high address of each range, so |address| is
+ // guaranteed to be lower than the range's high address. If |range| is
+ // not directly preceded by another range, it's possible for address to
+ // be below the range's low address, though. When that happens, address
+ // references something not within any range, so return false.
+
+ const Range *range = iterator.GetValuePtr();
+
+ // Make sure AddressType and EntryType are copyable basic types
+ // e.g.: integer types, pointers etc
+ if (address < range->base())
+ return false;
+
+ entry = range->entryptr();
+ if (entry_base)
+ *entry_base = range->base();
+ if (entry_size)
+ *entry_size = iterator.GetKey() - range->base() + 1;
+
+ return true;
+}
+
+
+template<typename AddressType, typename EntryType>
+bool StaticRangeMap<AddressType, EntryType>::RetrieveNearestRange(
+ const AddressType &address, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size) const {
+ // If address is within a range, RetrieveRange can handle it.
+ if (RetrieveRange(address, entry, entry_base, entry_size))
+ return true;
+
+ // upper_bound gives the first element whose key is greater than address,
+ // but we want the first element whose key is less than or equal to address.
+ // Decrement the iterator to get there, but not if the upper_bound already
+ // points to the beginning of the map - in that case, address is lower than
+ // the lowest stored key, so return false.
+
+ MapConstIterator iterator = map_.upper_bound(address);
+ if (iterator == map_.begin())
+ return false;
+ --iterator;
+
+ const Range *range = iterator.GetValuePtr();
+ entry = range->entryptr();
+ if (entry_base)
+ *entry_base = range->base();
+ if (entry_size)
+ *entry_size = iterator.GetKey() - range->base() + 1;
+
+ return true;
+}
+
+template<typename AddressType, typename EntryType>
+bool StaticRangeMap<AddressType, EntryType>::RetrieveRangeAtIndex(
+ int index, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size) const {
+
+ if (index >= GetCount()) {
+ BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount();
+ return false;
+ }
+
+ MapConstIterator iterator = map_.IteratorAtIndex(index);
+
+ const Range *range = iterator.GetValuePtr();
+
+ entry = range->entryptr();
+ if (entry_base)
+ *entry_base = range->base();
+ if (entry_size)
+ *entry_size = iterator.GetKey() - range->base() + 1;
+
+ return true;
+}
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_STATIC_RANGE_MAP_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_range_map.h b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map.h
new file mode 100644
index 000000000..91aabb032
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// static_range_map.h: StaticRangeMap.
+//
+// StaticRangeMap is similar as RangeMap. However, StaticRangeMap wraps a
+// StaticMap instead of std::map, and does not support dynamic operations like
+// StoreRange(...). StaticRangeMap provides same Retrieve*() interfaces as
+// RangeMap. Please see range_map.h for more documentation.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef PROCESSOR_STATIC_RANGE_MAP_H__
+#define PROCESSOR_STATIC_RANGE_MAP_H__
+
+
+#include "processor/static_map-inl.h"
+
+namespace google_breakpad {
+
+// AddressType is basic type, e.g.: integer types, pointers etc
+// EntryType could be a complex type, so we retrieve its pointer instead.
+template<typename AddressType, typename EntryType>
+class StaticRangeMap {
+ public:
+ StaticRangeMap(): map_() { }
+ explicit StaticRangeMap(const char *memory): map_(memory) { }
+
+ // Locates the range encompassing the supplied address. If there is
+ // no such range, returns false. entry_base and entry_size, if non-NULL,
+ // are set to the base and size of the entry's range.
+ bool RetrieveRange(const AddressType &address, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size) const;
+
+ // Locates the range encompassing the supplied address, if one exists.
+ // If no range encompasses the supplied address, locates the nearest range
+ // to the supplied address that is lower than the address. Returns false
+ // if no range meets these criteria. entry_base and entry_size, if
+ // non-NULL, are set to the base and size of the entry's range.
+ bool RetrieveNearestRange(const AddressType &address, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size)
+ const;
+
+ // Treating all ranges as a list ordered by the address spaces that they
+ // occupy, locates the range at the index specified by index. Returns
+ // false if index is larger than the number of ranges stored. entry_base
+ // and entry_size, if non-NULL, are set to the base and size of the entry's
+ // range.
+ //
+ // RetrieveRangeAtIndex is not optimized for speedy operation.
+ bool RetrieveRangeAtIndex(int index, const EntryType *&entry,
+ AddressType *entry_base, AddressType *entry_size)
+ const;
+
+ // Returns the number of ranges stored in the RangeMap.
+ inline int GetCount() const { return map_.size(); }
+
+ private:
+ friend class ModuleComparer;
+ class Range {
+ public:
+ AddressType base() const {
+ return *(reinterpret_cast<const AddressType*>(this));
+ }
+ const EntryType* entryptr() const {
+ return reinterpret_cast<const EntryType*>(this + sizeof(AddressType));
+ }
+ };
+
+ // Convenience types.
+ typedef StaticRangeMap* SelfPtr;
+ typedef StaticMap<AddressType, Range> AddressToRangeMap;
+ typedef typename AddressToRangeMap::const_iterator MapConstIterator;
+
+ AddressToRangeMap map_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STATIC_RANGE_MAP_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/static_range_map_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map_unittest.cc
new file mode 100644
index 000000000..282173622
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/static_range_map_unittest.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// static_range_map_unittest.cc: Unit tests for StaticRangeMap.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#include "breakpad_googletest_includes.h"
+#include "common/scoped_ptr.h"
+#include "processor/range_map-inl.h"
+#include "processor/static_range_map-inl.h"
+#include "processor/simple_serializer-inl.h"
+#include "processor/map_serializers-inl.h"
+#include "processor/logging.h"
+
+
+namespace {
+// Types used for testing.
+typedef int AddressType;
+typedef int EntryType;
+typedef google_breakpad::StaticRangeMap< AddressType, EntryType > TestMap;
+typedef google_breakpad::RangeMap< AddressType, EntryType > RMap;
+
+// RangeTest contains data to use for store and retrieve tests. See
+// RunTests for descriptions of the tests.
+struct RangeTest {
+ // Base address to use for test
+ AddressType address;
+
+ // Size of range to use for test
+ AddressType size;
+
+ // Unique ID of range - unstorable ranges must have unique IDs too
+ EntryType id;
+
+ // Whether this range is expected to be stored successfully or not
+ bool expect_storable;
+};
+
+// A RangeTestSet encompasses multiple RangeTests, which are run in
+// sequence on the same RangeMap.
+struct RangeTestSet {
+ // An array of RangeTests
+ const RangeTest* range_tests;
+
+ // The number of tests in the set
+ unsigned int range_test_count;
+};
+
+// These tests will be run sequentially. The first set of tests exercises
+// most functions of RangeTest, and verifies all of the bounds-checking.
+const RangeTest range_tests_0[] = {
+ { INT_MIN, 16, 1, true }, // lowest possible range
+ { -2, 5, 2, true }, // a range through zero
+ { INT_MAX - 9, 11, 3, false }, // tests anti-overflow
+ { INT_MAX - 9, 10, 4, true }, // highest possible range
+ { 5, 0, 5, false }, // tests anti-zero-size
+ { 5, 1, 6, true }, // smallest possible range
+ { -20, 15, 7, true }, // entirely negative
+
+ { 10, 10, 10, true }, // causes the following tests to fail
+ { 9, 10, 11, false }, // one-less base, one-less high
+ { 9, 11, 12, false }, // one-less base, identical high
+ { 9, 12, 13, false }, // completely contains existing
+ { 10, 9, 14, false }, // identical base, one-less high
+ { 10, 10, 15, false }, // exactly identical to existing range
+ { 10, 11, 16, false }, // identical base, one-greater high
+ { 11, 8, 17, false }, // contained completely within
+ { 11, 9, 18, false }, // one-greater base, identical high
+ { 11, 10, 19, false }, // one-greater base, one-greater high
+ { 9, 2, 20, false }, // overlaps bottom by one
+ { 10, 1, 21, false }, // overlaps bottom by one, contained
+ { 19, 1, 22, false }, // overlaps top by one, contained
+ { 19, 2, 23, false }, // overlaps top by one
+
+ { 9, 1, 24, true }, // directly below without overlap
+ { 20, 1, 25, true }, // directly above without overlap
+
+ { 6, 3, 26, true }, // exactly between two ranges, gapless
+ { 7, 3, 27, false }, // tries to span two ranges
+ { 7, 5, 28, false }, // tries to span three ranges
+ { 4, 20, 29, false }, // tries to contain several ranges
+
+ { 30, 50, 30, true },
+ { 90, 25, 31, true },
+ { 35, 65, 32, false }, // tries to span two noncontiguous
+ { 120, 10000, 33, true }, // > 8-bit
+ { 20000, 20000, 34, true }, // > 8-bit
+ { 0x10001, 0x10001, 35, true }, // > 16-bit
+
+ { 27, -1, 36, false } // tests high < base
+};
+
+// Attempt to fill the entire space. The entire space must be filled with
+// three stores because AddressType is signed for these tests, so RangeMap
+// treats the size as signed and rejects sizes that appear to be negative.
+// Even if these tests were run as unsigned, two stores would be needed
+// to fill the space because the entire size of the space could only be
+// described by using one more bit than would be present in AddressType.
+const RangeTest range_tests_1[] = {
+ { INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive
+ { -1, 2, 51, true }, // From -1 to 0, inclusive
+ { 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive
+ { INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice
+ { -1, 2, 54, false },
+ { 1, INT_MAX, 55, false },
+ { -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges
+};
+
+// A light round of testing to verify that RetrieveRange does the right
+// the right thing at the extremities of the range when nothing is stored
+// there. Checks are forced without storing anything at the extremities
+// by setting size = 0.
+const RangeTest range_tests_2[] = {
+ { INT_MIN, 0, 100, false }, // makes RetrieveRange check low end
+ { -1, 3, 101, true },
+ { INT_MAX, 0, 102, false }, // makes RetrieveRange check high end
+};
+
+// Similar to the previous test set, but with a couple of ranges closer
+// to the extremities.
+const RangeTest range_tests_3[] = {
+ { INT_MIN + 1, 1, 110, true },
+ { INT_MAX - 1, 1, 111, true },
+ { INT_MIN, 0, 112, false }, // makes RetrieveRange check low end
+ { INT_MAX, 0, 113, false } // makes RetrieveRange check high end
+};
+
+// The range map is cleared between sets of tests listed here.
+const RangeTestSet range_test_sets[] = {
+ { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) },
+ { range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) },
+ { range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) },
+ { range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) },
+ { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again
+};
+
+} // namespace
+
+namespace google_breakpad {
+class TestStaticRangeMap : public ::testing::Test {
+ protected:
+ void SetUp() {
+ kTestCasesCount_ = sizeof(range_test_sets) / sizeof(RangeTestSet);
+ }
+
+ // StoreTest uses the data in a RangeTest and calls StoreRange on the
+ // test RangeMap. It returns true if the expected result occurred, and
+ // false if something else happened.
+ void StoreTest(RMap* range_map, const RangeTest* range_test);
+
+ // RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
+ // test RangeMap. If it retrieves the expected value (which can be no
+ // map entry at the specified range,) it returns true, otherwise, it returns
+ // false. RetrieveTest will check the values around the base address and
+ // the high address of a range to guard against off-by-one errors.
+ void RetrieveTest(TestMap* range_map, const RangeTest* range_test);
+
+ // Test RetrieveRangeAtIndex, which is supposed to return objects in order
+ // according to their addresses. This test is performed by looping through
+ // the map, calling RetrieveRangeAtIndex for all possible indices in sequence,
+ // and verifying that each call returns a different object than the previous
+ // call, and that ranges are returned with increasing base addresses. Returns
+ // false if the test fails.
+ void RetrieveIndexTest(const TestMap* range_map, int set);
+
+ void RunTestCase(int test_case);
+
+ unsigned int kTestCasesCount_;
+ RangeMapSerializer<AddressType, EntryType> serializer_;
+};
+
+void TestStaticRangeMap::StoreTest(RMap* range_map,
+ const RangeTest* range_test) {
+ bool stored = range_map->StoreRange(range_test->address,
+ range_test->size,
+ range_test->id);
+ EXPECT_EQ(stored, range_test->expect_storable)
+ << "StoreRange id " << range_test->id << "FAILED";
+}
+
+void TestStaticRangeMap::RetrieveTest(TestMap* range_map,
+ const RangeTest* range_test) {
+ for (unsigned int side = 0; side <= 1; ++side) {
+ // When side == 0, check the low side (base address) of each range.
+ // When side == 1, check the high side (base + size) of each range.
+
+ // Check one-less and one-greater than the target address in addition
+ // to the target address itself.
+
+ // If the size of the range is only 1, don't check one greater than
+ // the base or one less than the high - for a successfully stored
+ // range, these tests would erroneously fail because the range is too
+ // small.
+ AddressType low_offset = -1;
+ AddressType high_offset = 1;
+ if (range_test->size == 1) {
+ if (!side) // When checking the low side,
+ high_offset = 0; // don't check one over the target.
+ else // When checking the high side,
+ low_offset = 0; // don't check one under the target.
+ }
+
+ for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
+ AddressType address =
+ offset +
+ (!side ? range_test->address :
+ range_test->address + range_test->size - 1);
+
+ bool expected_result = false; // This is correct for tests not stored.
+ if (range_test->expect_storable) {
+ if (offset == 0) // When checking the target address,
+ expected_result = true; // test should always succeed.
+ else if (offset == -1) // When checking one below the target,
+ expected_result = side; // should fail low and succeed high.
+ else // When checking one above the target,
+ expected_result = !side; // should succeed low and fail high.
+ }
+
+ const EntryType* id;
+ AddressType retrieved_base;
+ AddressType retrieved_size;
+ bool retrieved = range_map->RetrieveRange(address, id,
+ &retrieved_base,
+ &retrieved_size);
+
+ bool observed_result = retrieved && *id == range_test->id;
+ EXPECT_EQ(observed_result, expected_result)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+
+ // If a range was successfully retrieved, check that the returned
+ // bounds match the range as stored.
+ if (observed_result == true) {
+ EXPECT_EQ(retrieved_base, range_test->address)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+ EXPECT_EQ(retrieved_size, range_test->size)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+ }
+
+ // Now, check RetrieveNearestRange. The nearest range is always
+ // expected to be different from the test range when checking one
+ // less than the low side.
+ bool expected_nearest = range_test->expect_storable;
+ if (!side && offset < 0)
+ expected_nearest = false;
+
+ AddressType nearest_base;
+ AddressType nearest_size;
+ bool retrieved_nearest = range_map->RetrieveNearestRange(address,
+ id,
+ &nearest_base,
+ &nearest_size);
+
+ // When checking one greater than the high side, RetrieveNearestRange
+ // should usually return the test range. When a different range begins
+ // at that address, though, then RetrieveNearestRange should return the
+ // range at the address instead of the test range.
+ if (side && offset > 0 && nearest_base == address) {
+ expected_nearest = false;
+ }
+
+ bool observed_nearest = retrieved_nearest &&
+ *id == range_test->id;
+
+ EXPECT_EQ(observed_nearest, expected_nearest)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+
+ // If a range was successfully retrieved, check that the returned
+ // bounds match the range as stored.
+ if (expected_nearest ==true) {
+ EXPECT_EQ(nearest_base, range_test->address)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+ EXPECT_EQ(nearest_size, range_test->size)
+ << "RetrieveRange id " << range_test->id
+ << ", side " << side << ", offset " << offset << " FAILED.";
+ }
+ }
+ }
+}
+
+void TestStaticRangeMap::RetrieveIndexTest(const TestMap* range_map, int set) {
+ AddressType last_base = 0;
+ const EntryType* last_entry = 0;
+ const EntryType* entry;
+ int object_count = range_map->GetCount();
+ for (int object_index = 0; object_index < object_count; ++object_index) {
+ AddressType base;
+ ASSERT_TRUE(range_map->RetrieveRangeAtIndex(object_index,
+ entry,
+ &base,
+ NULL))
+ << "FAILED: RetrieveRangeAtIndex set " << set
+ << " index " << object_index;
+
+ ASSERT_TRUE(entry) << "FAILED: RetrieveRangeAtIndex set " << set
+ << " index " << object_index;
+
+ // It's impossible to do these comparisons unless there's a previous
+ // object to compare against.
+ if (last_entry) {
+ // The object must be different from the last_entry one.
+ EXPECT_NE(*entry, *last_entry) << "FAILED: RetrieveRangeAtIndex set "
+ << set << " index " << object_index;
+ // Each object must have a base greater than the previous object's base.
+ EXPECT_GT(base, last_base) << "FAILED: RetrieveRangeAtIndex set " << set
+ << " index " << object_index;
+ }
+ last_entry = entry;
+ last_base = base;
+ }
+
+ // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that
+ // are too high.
+ ASSERT_FALSE(range_map->RetrieveRangeAtIndex(
+ object_count, entry, NULL, NULL)) << "FAILED: RetrieveRangeAtIndex set "
+ << set << " index " << object_count
+ << " (too large)";
+}
+
+// RunTests runs a series of test sets.
+void TestStaticRangeMap::RunTestCase(int test_case) {
+ // Maintain the range map in a pointer so that deletion can be meaningfully
+ // tested.
+ scoped_ptr<RMap> rmap(new RMap());
+
+ const RangeTest* range_tests = range_test_sets[test_case].range_tests;
+ unsigned int range_test_count = range_test_sets[test_case].range_test_count;
+
+ // Run the StoreRange test, which validates StoreRange and initializes
+ // the RangeMap with data for the RetrieveRange test.
+ int stored_count = 0; // The number of ranges successfully stored
+ for (unsigned int range_test_index = 0;
+ range_test_index < range_test_count;
+ ++range_test_index) {
+ const RangeTest* range_test = &range_tests[range_test_index];
+ StoreTest(rmap.get(), range_test);
+
+ if (range_test->expect_storable)
+ ++stored_count;
+ }
+
+ scoped_array<char> memaddr(serializer_.Serialize(*rmap, NULL));
+ scoped_ptr<TestMap> static_range_map(new TestMap(memaddr.get()));
+
+ // The RangeMap's own count of objects should also match.
+ EXPECT_EQ(static_range_map->GetCount(), stored_count);
+
+ // Run the RetrieveRange test
+ for (unsigned int range_test_index = 0;
+ range_test_index < range_test_count;
+ ++range_test_index) {
+ const RangeTest* range_test = &range_tests[range_test_index];
+ RetrieveTest(static_range_map.get(), range_test);
+ }
+
+ RetrieveIndexTest(static_range_map.get(), test_case);
+}
+
+TEST_F(TestStaticRangeMap, TestCase0) {
+ int test_case = 0;
+ RunTestCase(test_case);
+}
+
+TEST_F(TestStaticRangeMap, TestCase1) {
+ int test_case = 1;
+ RunTestCase(test_case);
+}
+
+TEST_F(TestStaticRangeMap, TestCase2) {
+ int test_case = 2;
+ RunTestCase(test_case);
+}
+
+TEST_F(TestStaticRangeMap, TestCase3) {
+ int test_case = 3;
+ RunTestCase(test_case);
+}
+
+TEST_F(TestStaticRangeMap, RunTestCase0Again) {
+ int test_case = 0;
+ RunTestCase(test_case);
+}
+
+} // namespace google_breakpad
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.cc b/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.cc
new file mode 100644
index 000000000..a6ee26a26
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.cc
@@ -0,0 +1,6418 @@
+// Copyright (c) 2015 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ntstatus_reason_win.h: Windows NTSTATUS code to string.
+//
+// Provides a means to convert NTSTATUS codes to strings.
+//
+// Author: Ben Wagner
+
+#include <string>
+
+#include "common/stdio_wrapper.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_exception_win32.h"
+#include "processor/symbolic_constants_win.h"
+
+namespace google_breakpad {
+
+string NTStatusToString(uint32_t ntstatus) {
+ string reason;
+ // The content of this switch was created from ntstatus.h in the 8.1 SDK with
+ //
+ // egrep '#define [A-Z_0-9]+\s+\(\(NTSTATUS\)0xC[0-9A-F]+L\)' ntstatus.h
+ // | tr -d '\r'
+ // | sed -r 's@#define ([A-Z_0-9]+)\s+\(\(NTSTATUS\)(0xC[0-9A-F]+)L\).*@\2 \1@'
+ // | sort
+ // | sed -r 's@(0xC[0-9A-F]+) ([A-Z_0-9]+)@ case MD_NTSTATUS_WIN_\2:\n reason = "\2";\n break;@'
+ //
+ // With easy copy to clipboard with
+ // | xclip -selection c # on linux
+ // | clip # on windows
+ // | pbcopy # on mac
+ //
+ // and then the default case added.
+ switch (ntstatus) {
+ case MD_NTSTATUS_WIN_STATUS_UNSUCCESSFUL:
+ reason = "STATUS_UNSUCCESSFUL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_IMPLEMENTED:
+ reason = "STATUS_NOT_IMPLEMENTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_INFO_CLASS:
+ reason = "STATUS_INVALID_INFO_CLASS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INFO_LENGTH_MISMATCH:
+ reason = "STATUS_INFO_LENGTH_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_VIOLATION:
+ reason = "STATUS_ACCESS_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IN_PAGE_ERROR:
+ reason = "STATUS_IN_PAGE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PAGEFILE_QUOTA:
+ reason = "STATUS_PAGEFILE_QUOTA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_HANDLE:
+ reason = "STATUS_INVALID_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_INITIAL_STACK:
+ reason = "STATUS_BAD_INITIAL_STACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_INITIAL_PC:
+ reason = "STATUS_BAD_INITIAL_PC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_CID:
+ reason = "STATUS_INVALID_CID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TIMER_NOT_CANCELED:
+ reason = "STATUS_TIMER_NOT_CANCELED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER:
+ reason = "STATUS_INVALID_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_DEVICE:
+ reason = "STATUS_NO_SUCH_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_FILE:
+ reason = "STATUS_NO_SUCH_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_REQUEST:
+ reason = "STATUS_INVALID_DEVICE_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_END_OF_FILE:
+ reason = "STATUS_END_OF_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_VOLUME:
+ reason = "STATUS_WRONG_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_MEDIA_IN_DEVICE:
+ reason = "STATUS_NO_MEDIA_IN_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNRECOGNIZED_MEDIA:
+ reason = "STATUS_UNRECOGNIZED_MEDIA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NONEXISTENT_SECTOR:
+ reason = "STATUS_NONEXISTENT_SECTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MORE_PROCESSING_REQUIRED:
+ reason = "STATUS_MORE_PROCESSING_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_MEMORY:
+ reason = "STATUS_NO_MEMORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONFLICTING_ADDRESSES:
+ reason = "STATUS_CONFLICTING_ADDRESSES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_MAPPED_VIEW:
+ reason = "STATUS_NOT_MAPPED_VIEW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNABLE_TO_FREE_VM:
+ reason = "STATUS_UNABLE_TO_FREE_VM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNABLE_TO_DELETE_SECTION:
+ reason = "STATUS_UNABLE_TO_DELETE_SECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SYSTEM_SERVICE:
+ reason = "STATUS_INVALID_SYSTEM_SERVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_INSTRUCTION:
+ reason = "STATUS_ILLEGAL_INSTRUCTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LOCK_SEQUENCE:
+ reason = "STATUS_INVALID_LOCK_SEQUENCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_VIEW_SIZE:
+ reason = "STATUS_INVALID_VIEW_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_FILE_FOR_SECTION:
+ reason = "STATUS_INVALID_FILE_FOR_SECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALREADY_COMMITTED:
+ reason = "STATUS_ALREADY_COMMITTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DENIED:
+ reason = "STATUS_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BUFFER_TOO_SMALL:
+ reason = "STATUS_BUFFER_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_TYPE_MISMATCH:
+ reason = "STATUS_OBJECT_TYPE_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NONCONTINUABLE_EXCEPTION:
+ reason = "STATUS_NONCONTINUABLE_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DISPOSITION:
+ reason = "STATUS_INVALID_DISPOSITION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNWIND:
+ reason = "STATUS_UNWIND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_STACK:
+ reason = "STATUS_BAD_STACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_UNWIND_TARGET:
+ reason = "STATUS_INVALID_UNWIND_TARGET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_LOCKED:
+ reason = "STATUS_NOT_LOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PARITY_ERROR:
+ reason = "STATUS_PARITY_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNABLE_TO_DECOMMIT_VM:
+ reason = "STATUS_UNABLE_TO_DECOMMIT_VM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_COMMITTED:
+ reason = "STATUS_NOT_COMMITTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PORT_ATTRIBUTES:
+ reason = "STATUS_INVALID_PORT_ATTRIBUTES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_MESSAGE_TOO_LONG:
+ reason = "STATUS_PORT_MESSAGE_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_MIX:
+ reason = "STATUS_INVALID_PARAMETER_MIX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_QUOTA_LOWER:
+ reason = "STATUS_INVALID_QUOTA_LOWER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_CORRUPT_ERROR:
+ reason = "STATUS_DISK_CORRUPT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_INVALID:
+ reason = "STATUS_OBJECT_NAME_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_NOT_FOUND:
+ reason = "STATUS_OBJECT_NAME_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_COLLISION:
+ reason = "STATUS_OBJECT_NAME_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_DISCONNECTED:
+ reason = "STATUS_PORT_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_ALREADY_ATTACHED:
+ reason = "STATUS_DEVICE_ALREADY_ATTACHED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_INVALID:
+ reason = "STATUS_OBJECT_PATH_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_NOT_FOUND:
+ reason = "STATUS_OBJECT_PATH_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_SYNTAX_BAD:
+ reason = "STATUS_OBJECT_PATH_SYNTAX_BAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATA_OVERRUN:
+ reason = "STATUS_DATA_OVERRUN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATA_LATE_ERROR:
+ reason = "STATUS_DATA_LATE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATA_ERROR:
+ reason = "STATUS_DATA_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CRC_ERROR:
+ reason = "STATUS_CRC_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECTION_TOO_BIG:
+ reason = "STATUS_SECTION_TOO_BIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_CONNECTION_REFUSED:
+ reason = "STATUS_PORT_CONNECTION_REFUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PORT_HANDLE:
+ reason = "STATUS_INVALID_PORT_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHARING_VIOLATION:
+ reason = "STATUS_SHARING_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_QUOTA_EXCEEDED:
+ reason = "STATUS_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PAGE_PROTECTION:
+ reason = "STATUS_INVALID_PAGE_PROTECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUTANT_NOT_OWNED:
+ reason = "STATUS_MUTANT_NOT_OWNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SEMAPHORE_LIMIT_EXCEEDED:
+ reason = "STATUS_SEMAPHORE_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_ALREADY_SET:
+ reason = "STATUS_PORT_ALREADY_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECTION_NOT_IMAGE:
+ reason = "STATUS_SECTION_NOT_IMAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SUSPEND_COUNT_EXCEEDED:
+ reason = "STATUS_SUSPEND_COUNT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREAD_IS_TERMINATING:
+ reason = "STATUS_THREAD_IS_TERMINATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_WORKING_SET_LIMIT:
+ reason = "STATUS_BAD_WORKING_SET_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_FILE_MAP:
+ reason = "STATUS_INCOMPATIBLE_FILE_MAP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECTION_PROTECTION:
+ reason = "STATUS_SECTION_PROTECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EAS_NOT_SUPPORTED:
+ reason = "STATUS_EAS_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EA_TOO_LARGE:
+ reason = "STATUS_EA_TOO_LARGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NONEXISTENT_EA_ENTRY:
+ reason = "STATUS_NONEXISTENT_EA_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_EAS_ON_FILE:
+ reason = "STATUS_NO_EAS_ON_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EA_CORRUPT_ERROR:
+ reason = "STATUS_EA_CORRUPT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_LOCK_CONFLICT:
+ reason = "STATUS_FILE_LOCK_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOCK_NOT_GRANTED:
+ reason = "STATUS_LOCK_NOT_GRANTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DELETE_PENDING:
+ reason = "STATUS_DELETE_PENDING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTL_FILE_NOT_SUPPORTED:
+ reason = "STATUS_CTL_FILE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNKNOWN_REVISION:
+ reason = "STATUS_UNKNOWN_REVISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REVISION_MISMATCH:
+ reason = "STATUS_REVISION_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_OWNER:
+ reason = "STATUS_INVALID_OWNER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PRIMARY_GROUP:
+ reason = "STATUS_INVALID_PRIMARY_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_IMPERSONATION_TOKEN:
+ reason = "STATUS_NO_IMPERSONATION_TOKEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_DISABLE_MANDATORY:
+ reason = "STATUS_CANT_DISABLE_MANDATORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_LOGON_SERVERS:
+ reason = "STATUS_NO_LOGON_SERVERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_LOGON_SESSION:
+ reason = "STATUS_NO_SUCH_LOGON_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_PRIVILEGE:
+ reason = "STATUS_NO_SUCH_PRIVILEGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PRIVILEGE_NOT_HELD:
+ reason = "STATUS_PRIVILEGE_NOT_HELD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ACCOUNT_NAME:
+ reason = "STATUS_INVALID_ACCOUNT_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_USER_EXISTS:
+ reason = "STATUS_USER_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_USER:
+ reason = "STATUS_NO_SUCH_USER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GROUP_EXISTS:
+ reason = "STATUS_GROUP_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_GROUP:
+ reason = "STATUS_NO_SUCH_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMBER_IN_GROUP:
+ reason = "STATUS_MEMBER_IN_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMBER_NOT_IN_GROUP:
+ reason = "STATUS_MEMBER_NOT_IN_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LAST_ADMIN:
+ reason = "STATUS_LAST_ADMIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_PASSWORD:
+ reason = "STATUS_WRONG_PASSWORD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILL_FORMED_PASSWORD:
+ reason = "STATUS_ILL_FORMED_PASSWORD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PASSWORD_RESTRICTION:
+ reason = "STATUS_PASSWORD_RESTRICTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_FAILURE:
+ reason = "STATUS_LOGON_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCOUNT_RESTRICTION:
+ reason = "STATUS_ACCOUNT_RESTRICTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LOGON_HOURS:
+ reason = "STATUS_INVALID_LOGON_HOURS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_WORKSTATION:
+ reason = "STATUS_INVALID_WORKSTATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PASSWORD_EXPIRED:
+ reason = "STATUS_PASSWORD_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCOUNT_DISABLED:
+ reason = "STATUS_ACCOUNT_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NONE_MAPPED:
+ reason = "STATUS_NONE_MAPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_LUIDS_REQUESTED:
+ reason = "STATUS_TOO_MANY_LUIDS_REQUESTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LUIDS_EXHAUSTED:
+ reason = "STATUS_LUIDS_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SUB_AUTHORITY:
+ reason = "STATUS_INVALID_SUB_AUTHORITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ACL:
+ reason = "STATUS_INVALID_ACL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SID:
+ reason = "STATUS_INVALID_SID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SECURITY_DESCR:
+ reason = "STATUS_INVALID_SECURITY_DESCR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROCEDURE_NOT_FOUND:
+ reason = "STATUS_PROCEDURE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_FORMAT:
+ reason = "STATUS_INVALID_IMAGE_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_TOKEN:
+ reason = "STATUS_NO_TOKEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_INHERITANCE_ACL:
+ reason = "STATUS_BAD_INHERITANCE_ACL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RANGE_NOT_LOCKED:
+ reason = "STATUS_RANGE_NOT_LOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_FULL:
+ reason = "STATUS_DISK_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERVER_DISABLED:
+ reason = "STATUS_SERVER_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERVER_NOT_DISABLED:
+ reason = "STATUS_SERVER_NOT_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_GUIDS_REQUESTED:
+ reason = "STATUS_TOO_MANY_GUIDS_REQUESTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GUIDS_EXHAUSTED:
+ reason = "STATUS_GUIDS_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ID_AUTHORITY:
+ reason = "STATUS_INVALID_ID_AUTHORITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AGENTS_EXHAUSTED:
+ reason = "STATUS_AGENTS_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_VOLUME_LABEL:
+ reason = "STATUS_INVALID_VOLUME_LABEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECTION_NOT_EXTENDED:
+ reason = "STATUS_SECTION_NOT_EXTENDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_MAPPED_DATA:
+ reason = "STATUS_NOT_MAPPED_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_DATA_NOT_FOUND:
+ reason = "STATUS_RESOURCE_DATA_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_TYPE_NOT_FOUND:
+ reason = "STATUS_RESOURCE_TYPE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_NAME_NOT_FOUND:
+ reason = "STATUS_RESOURCE_NAME_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ARRAY_BOUNDS_EXCEEDED:
+ reason = "STATUS_ARRAY_BOUNDS_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_DENORMAL_OPERAND:
+ reason = "STATUS_FLOAT_DENORMAL_OPERAND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_DIVIDE_BY_ZERO:
+ reason = "STATUS_FLOAT_DIVIDE_BY_ZERO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_INEXACT_RESULT:
+ reason = "STATUS_FLOAT_INEXACT_RESULT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_INVALID_OPERATION:
+ reason = "STATUS_FLOAT_INVALID_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_OVERFLOW:
+ reason = "STATUS_FLOAT_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_STACK_CHECK:
+ reason = "STATUS_FLOAT_STACK_CHECK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_UNDERFLOW:
+ reason = "STATUS_FLOAT_UNDERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTEGER_DIVIDE_BY_ZERO:
+ reason = "STATUS_INTEGER_DIVIDE_BY_ZERO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTEGER_OVERFLOW:
+ reason = "STATUS_INTEGER_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PRIVILEGED_INSTRUCTION:
+ reason = "STATUS_PRIVILEGED_INSTRUCTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_PAGING_FILES:
+ reason = "STATUS_TOO_MANY_PAGING_FILES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_INVALID:
+ reason = "STATUS_FILE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALLOTTED_SPACE_EXCEEDED:
+ reason = "STATUS_ALLOTTED_SPACE_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_RESOURCES:
+ reason = "STATUS_INSUFFICIENT_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DFS_EXIT_PATH_FOUND:
+ reason = "STATUS_DFS_EXIT_PATH_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_DATA_ERROR:
+ reason = "STATUS_DEVICE_DATA_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_CONNECTED:
+ reason = "STATUS_DEVICE_NOT_CONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_POWER_FAILURE:
+ reason = "STATUS_DEVICE_POWER_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FREE_VM_NOT_AT_BASE:
+ reason = "STATUS_FREE_VM_NOT_AT_BASE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMORY_NOT_ALLOCATED:
+ reason = "STATUS_MEMORY_NOT_ALLOCATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WORKING_SET_QUOTA:
+ reason = "STATUS_WORKING_SET_QUOTA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEDIA_WRITE_PROTECTED:
+ reason = "STATUS_MEDIA_WRITE_PROTECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_READY:
+ reason = "STATUS_DEVICE_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_GROUP_ATTRIBUTES:
+ reason = "STATUS_INVALID_GROUP_ATTRIBUTES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_IMPERSONATION_LEVEL:
+ reason = "STATUS_BAD_IMPERSONATION_LEVEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_OPEN_ANONYMOUS:
+ reason = "STATUS_CANT_OPEN_ANONYMOUS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_VALIDATION_CLASS:
+ reason = "STATUS_BAD_VALIDATION_CLASS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_TOKEN_TYPE:
+ reason = "STATUS_BAD_TOKEN_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_MASTER_BOOT_RECORD:
+ reason = "STATUS_BAD_MASTER_BOOT_RECORD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSTRUCTION_MISALIGNMENT:
+ reason = "STATUS_INSTRUCTION_MISALIGNMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSTANCE_NOT_AVAILABLE:
+ reason = "STATUS_INSTANCE_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_NOT_AVAILABLE:
+ reason = "STATUS_PIPE_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PIPE_STATE:
+ reason = "STATUS_INVALID_PIPE_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_BUSY:
+ reason = "STATUS_PIPE_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_FUNCTION:
+ reason = "STATUS_ILLEGAL_FUNCTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_DISCONNECTED:
+ reason = "STATUS_PIPE_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_CLOSING:
+ reason = "STATUS_PIPE_CLOSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_CONNECTED:
+ reason = "STATUS_PIPE_CONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_LISTENING:
+ reason = "STATUS_PIPE_LISTENING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_READ_MODE:
+ reason = "STATUS_INVALID_READ_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_TIMEOUT:
+ reason = "STATUS_IO_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_FORCED_CLOSED:
+ reason = "STATUS_FILE_FORCED_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROFILING_NOT_STARTED:
+ reason = "STATUS_PROFILING_NOT_STARTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROFILING_NOT_STOPPED:
+ reason = "STATUS_PROFILING_NOT_STOPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COULD_NOT_INTERPRET:
+ reason = "STATUS_COULD_NOT_INTERPRET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_IS_A_DIRECTORY:
+ reason = "STATUS_FILE_IS_A_DIRECTORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED:
+ reason = "STATUS_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_NOT_LISTENING:
+ reason = "STATUS_REMOTE_NOT_LISTENING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DUPLICATE_NAME:
+ reason = "STATUS_DUPLICATE_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_NETWORK_PATH:
+ reason = "STATUS_BAD_NETWORK_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_BUSY:
+ reason = "STATUS_NETWORK_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_DOES_NOT_EXIST:
+ reason = "STATUS_DEVICE_DOES_NOT_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_COMMANDS:
+ reason = "STATUS_TOO_MANY_COMMANDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADAPTER_HARDWARE_ERROR:
+ reason = "STATUS_ADAPTER_HARDWARE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_NETWORK_RESPONSE:
+ reason = "STATUS_INVALID_NETWORK_RESPONSE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNEXPECTED_NETWORK_ERROR:
+ reason = "STATUS_UNEXPECTED_NETWORK_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_REMOTE_ADAPTER:
+ reason = "STATUS_BAD_REMOTE_ADAPTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PRINT_QUEUE_FULL:
+ reason = "STATUS_PRINT_QUEUE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SPOOL_SPACE:
+ reason = "STATUS_NO_SPOOL_SPACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PRINT_CANCELLED:
+ reason = "STATUS_PRINT_CANCELLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_NAME_DELETED:
+ reason = "STATUS_NETWORK_NAME_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_ACCESS_DENIED:
+ reason = "STATUS_NETWORK_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_DEVICE_TYPE:
+ reason = "STATUS_BAD_DEVICE_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_NETWORK_NAME:
+ reason = "STATUS_BAD_NETWORK_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_NAMES:
+ reason = "STATUS_TOO_MANY_NAMES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_SESSIONS:
+ reason = "STATUS_TOO_MANY_SESSIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHARING_PAUSED:
+ reason = "STATUS_SHARING_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REQUEST_NOT_ACCEPTED:
+ reason = "STATUS_REQUEST_NOT_ACCEPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REDIRECTOR_PAUSED:
+ reason = "STATUS_REDIRECTOR_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NET_WRITE_FAULT:
+ reason = "STATUS_NET_WRITE_FAULT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROFILING_AT_LIMIT:
+ reason = "STATUS_PROFILING_AT_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SAME_DEVICE:
+ reason = "STATUS_NOT_SAME_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_RENAMED:
+ reason = "STATUS_FILE_RENAMED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTUAL_CIRCUIT_CLOSED:
+ reason = "STATUS_VIRTUAL_CIRCUIT_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SECURITY_ON_OBJECT:
+ reason = "STATUS_NO_SECURITY_ON_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_WAIT:
+ reason = "STATUS_CANT_WAIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_EMPTY:
+ reason = "STATUS_PIPE_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_ACCESS_DOMAIN_INFO:
+ reason = "STATUS_CANT_ACCESS_DOMAIN_INFO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_TERMINATE_SELF:
+ reason = "STATUS_CANT_TERMINATE_SELF";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SERVER_STATE:
+ reason = "STATUS_INVALID_SERVER_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DOMAIN_STATE:
+ reason = "STATUS_INVALID_DOMAIN_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DOMAIN_ROLE:
+ reason = "STATUS_INVALID_DOMAIN_ROLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_DOMAIN:
+ reason = "STATUS_NO_SUCH_DOMAIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOMAIN_EXISTS:
+ reason = "STATUS_DOMAIN_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOMAIN_LIMIT_EXCEEDED:
+ reason = "STATUS_DOMAIN_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OPLOCK_NOT_GRANTED:
+ reason = "STATUS_OPLOCK_NOT_GRANTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_OPLOCK_PROTOCOL:
+ reason = "STATUS_INVALID_OPLOCK_PROTOCOL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTERNAL_DB_CORRUPTION:
+ reason = "STATUS_INTERNAL_DB_CORRUPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTERNAL_ERROR:
+ reason = "STATUS_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GENERIC_NOT_MAPPED:
+ reason = "STATUS_GENERIC_NOT_MAPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_DESCRIPTOR_FORMAT:
+ reason = "STATUS_BAD_DESCRIPTOR_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_USER_BUFFER:
+ reason = "STATUS_INVALID_USER_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNEXPECTED_IO_ERROR:
+ reason = "STATUS_UNEXPECTED_IO_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_CREATE_ERR:
+ reason = "STATUS_UNEXPECTED_MM_CREATE_ERR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_MAP_ERROR:
+ reason = "STATUS_UNEXPECTED_MM_MAP_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_EXTEND_ERR:
+ reason = "STATUS_UNEXPECTED_MM_EXTEND_ERR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_LOGON_PROCESS:
+ reason = "STATUS_NOT_LOGON_PROCESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_SESSION_EXISTS:
+ reason = "STATUS_LOGON_SESSION_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_1:
+ reason = "STATUS_INVALID_PARAMETER_1";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_2:
+ reason = "STATUS_INVALID_PARAMETER_2";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_3:
+ reason = "STATUS_INVALID_PARAMETER_3";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_4:
+ reason = "STATUS_INVALID_PARAMETER_4";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_5:
+ reason = "STATUS_INVALID_PARAMETER_5";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_6:
+ reason = "STATUS_INVALID_PARAMETER_6";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_7:
+ reason = "STATUS_INVALID_PARAMETER_7";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_8:
+ reason = "STATUS_INVALID_PARAMETER_8";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_9:
+ reason = "STATUS_INVALID_PARAMETER_9";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_10:
+ reason = "STATUS_INVALID_PARAMETER_10";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_11:
+ reason = "STATUS_INVALID_PARAMETER_11";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_12:
+ reason = "STATUS_INVALID_PARAMETER_12";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REDIRECTOR_NOT_STARTED:
+ reason = "STATUS_REDIRECTOR_NOT_STARTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REDIRECTOR_STARTED:
+ reason = "STATUS_REDIRECTOR_STARTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STACK_OVERFLOW:
+ reason = "STATUS_STACK_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_PACKAGE:
+ reason = "STATUS_NO_SUCH_PACKAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_FUNCTION_TABLE:
+ reason = "STATUS_BAD_FUNCTION_TABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VARIABLE_NOT_FOUND:
+ reason = "STATUS_VARIABLE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_EMPTY:
+ reason = "STATUS_DIRECTORY_NOT_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_CORRUPT_ERROR:
+ reason = "STATUS_FILE_CORRUPT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_A_DIRECTORY:
+ reason = "STATUS_NOT_A_DIRECTORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_LOGON_SESSION_STATE:
+ reason = "STATUS_BAD_LOGON_SESSION_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_SESSION_COLLISION:
+ reason = "STATUS_LOGON_SESSION_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NAME_TOO_LONG:
+ reason = "STATUS_NAME_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILES_OPEN:
+ reason = "STATUS_FILES_OPEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_IN_USE:
+ reason = "STATUS_CONNECTION_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MESSAGE_NOT_FOUND:
+ reason = "STATUS_MESSAGE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROCESS_IS_TERMINATING:
+ reason = "STATUS_PROCESS_IS_TERMINATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LOGON_TYPE:
+ reason = "STATUS_INVALID_LOGON_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_GUID_TRANSLATION:
+ reason = "STATUS_NO_GUID_TRANSLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_IMPERSONATE:
+ reason = "STATUS_CANNOT_IMPERSONATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_ALREADY_LOADED:
+ reason = "STATUS_IMAGE_ALREADY_LOADED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_NOT_PRESENT:
+ reason = "STATUS_ABIOS_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_LID_NOT_EXIST:
+ reason = "STATUS_ABIOS_LID_NOT_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_LID_ALREADY_OWNED:
+ reason = "STATUS_ABIOS_LID_ALREADY_OWNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_NOT_LID_OWNER:
+ reason = "STATUS_ABIOS_NOT_LID_OWNER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_COMMAND:
+ reason = "STATUS_ABIOS_INVALID_COMMAND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_LID:
+ reason = "STATUS_ABIOS_INVALID_LID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE:
+ reason = "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_SELECTOR:
+ reason = "STATUS_ABIOS_INVALID_SELECTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_LDT:
+ reason = "STATUS_NO_LDT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LDT_SIZE:
+ reason = "STATUS_INVALID_LDT_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LDT_OFFSET:
+ reason = "STATUS_INVALID_LDT_OFFSET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LDT_DESCRIPTOR:
+ reason = "STATUS_INVALID_LDT_DESCRIPTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_NE_FORMAT:
+ reason = "STATUS_INVALID_IMAGE_NE_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RXACT_INVALID_STATE:
+ reason = "STATUS_RXACT_INVALID_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RXACT_COMMIT_FAILURE:
+ reason = "STATUS_RXACT_COMMIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MAPPED_FILE_SIZE_ZERO:
+ reason = "STATUS_MAPPED_FILE_SIZE_ZERO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_OPENED_FILES:
+ reason = "STATUS_TOO_MANY_OPENED_FILES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANCELLED:
+ reason = "STATUS_CANCELLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_DELETE:
+ reason = "STATUS_CANNOT_DELETE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_COMPUTER_NAME:
+ reason = "STATUS_INVALID_COMPUTER_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_DELETED:
+ reason = "STATUS_FILE_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPECIAL_ACCOUNT:
+ reason = "STATUS_SPECIAL_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPECIAL_GROUP:
+ reason = "STATUS_SPECIAL_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPECIAL_USER:
+ reason = "STATUS_SPECIAL_USER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMBERS_PRIMARY_GROUP:
+ reason = "STATUS_MEMBERS_PRIMARY_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_CLOSED:
+ reason = "STATUS_FILE_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_THREADS:
+ reason = "STATUS_TOO_MANY_THREADS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREAD_NOT_IN_PROCESS:
+ reason = "STATUS_THREAD_NOT_IN_PROCESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOKEN_ALREADY_IN_USE:
+ reason = "STATUS_TOKEN_ALREADY_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PAGEFILE_QUOTA_EXCEEDED:
+ reason = "STATUS_PAGEFILE_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COMMITMENT_LIMIT:
+ reason = "STATUS_COMMITMENT_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_LE_FORMAT:
+ reason = "STATUS_INVALID_IMAGE_LE_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_NOT_MZ:
+ reason = "STATUS_INVALID_IMAGE_NOT_MZ";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_PROTECT:
+ reason = "STATUS_INVALID_IMAGE_PROTECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_16:
+ reason = "STATUS_INVALID_IMAGE_WIN_16";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_SERVER_CONFLICT:
+ reason = "STATUS_LOGON_SERVER_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TIME_DIFFERENCE_AT_DC:
+ reason = "STATUS_TIME_DIFFERENCE_AT_DC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYNCHRONIZATION_REQUIRED:
+ reason = "STATUS_SYNCHRONIZATION_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DLL_NOT_FOUND:
+ reason = "STATUS_DLL_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OPEN_FAILED:
+ reason = "STATUS_OPEN_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_PRIVILEGE_FAILED:
+ reason = "STATUS_IO_PRIVILEGE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ORDINAL_NOT_FOUND:
+ reason = "STATUS_ORDINAL_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENTRYPOINT_NOT_FOUND:
+ reason = "STATUS_ENTRYPOINT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONTROL_C_EXIT:
+ reason = "STATUS_CONTROL_C_EXIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOCAL_DISCONNECT:
+ reason = "STATUS_LOCAL_DISCONNECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_DISCONNECT:
+ reason = "STATUS_REMOTE_DISCONNECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_RESOURCES:
+ reason = "STATUS_REMOTE_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LINK_FAILED:
+ reason = "STATUS_LINK_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LINK_TIMEOUT:
+ reason = "STATUS_LINK_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_CONNECTION:
+ reason = "STATUS_INVALID_CONNECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS:
+ reason = "STATUS_INVALID_ADDRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DLL_INIT_FAILED:
+ reason = "STATUS_DLL_INIT_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MISSING_SYSTEMFILE:
+ reason = "STATUS_MISSING_SYSTEMFILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNHANDLED_EXCEPTION:
+ reason = "STATUS_UNHANDLED_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_INIT_FAILURE:
+ reason = "STATUS_APP_INIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PAGEFILE_CREATE_FAILED:
+ reason = "STATUS_PAGEFILE_CREATE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_PAGEFILE:
+ reason = "STATUS_NO_PAGEFILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LEVEL:
+ reason = "STATUS_INVALID_LEVEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_PASSWORD_CORE:
+ reason = "STATUS_WRONG_PASSWORD_CORE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_FLOAT_CONTEXT:
+ reason = "STATUS_ILLEGAL_FLOAT_CONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PIPE_BROKEN:
+ reason = "STATUS_PIPE_BROKEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REGISTRY_CORRUPT:
+ reason = "STATUS_REGISTRY_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REGISTRY_IO_FAILED:
+ reason = "STATUS_REGISTRY_IO_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_EVENT_PAIR:
+ reason = "STATUS_NO_EVENT_PAIR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNRECOGNIZED_VOLUME:
+ reason = "STATUS_UNRECOGNIZED_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERIAL_NO_DEVICE_INITED:
+ reason = "STATUS_SERIAL_NO_DEVICE_INITED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_ALIAS:
+ reason = "STATUS_NO_SUCH_ALIAS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMBER_NOT_IN_ALIAS:
+ reason = "STATUS_MEMBER_NOT_IN_ALIAS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MEMBER_IN_ALIAS:
+ reason = "STATUS_MEMBER_IN_ALIAS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALIAS_EXISTS:
+ reason = "STATUS_ALIAS_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_NOT_GRANTED:
+ reason = "STATUS_LOGON_NOT_GRANTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_SECRETS:
+ reason = "STATUS_TOO_MANY_SECRETS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECRET_TOO_LONG:
+ reason = "STATUS_SECRET_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTERNAL_DB_ERROR:
+ reason = "STATUS_INTERNAL_DB_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FULLSCREEN_MODE:
+ reason = "STATUS_FULLSCREEN_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_CONTEXT_IDS:
+ reason = "STATUS_TOO_MANY_CONTEXT_IDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGON_TYPE_NOT_GRANTED:
+ reason = "STATUS_LOGON_TYPE_NOT_GRANTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_REGISTRY_FILE:
+ reason = "STATUS_NOT_REGISTRY_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NT_CROSS_ENCRYPTION_REQUIRED:
+ reason = "STATUS_NT_CROSS_ENCRYPTION_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOMAIN_CTRLR_CONFIG_ERROR:
+ reason = "STATUS_DOMAIN_CTRLR_CONFIG_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FT_MISSING_MEMBER:
+ reason = "STATUS_FT_MISSING_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILL_FORMED_SERVICE_ENTRY:
+ reason = "STATUS_ILL_FORMED_SERVICE_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_CHARACTER:
+ reason = "STATUS_ILLEGAL_CHARACTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNMAPPABLE_CHARACTER:
+ reason = "STATUS_UNMAPPABLE_CHARACTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNDEFINED_CHARACTER:
+ reason = "STATUS_UNDEFINED_CHARACTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOPPY_VOLUME:
+ reason = "STATUS_FLOPPY_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOPPY_ID_MARK_NOT_FOUND:
+ reason = "STATUS_FLOPPY_ID_MARK_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOPPY_WRONG_CYLINDER:
+ reason = "STATUS_FLOPPY_WRONG_CYLINDER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOPPY_UNKNOWN_ERROR:
+ reason = "STATUS_FLOPPY_UNKNOWN_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOPPY_BAD_REGISTERS:
+ reason = "STATUS_FLOPPY_BAD_REGISTERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_RECALIBRATE_FAILED:
+ reason = "STATUS_DISK_RECALIBRATE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_OPERATION_FAILED:
+ reason = "STATUS_DISK_OPERATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_RESET_FAILED:
+ reason = "STATUS_DISK_RESET_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHARED_IRQ_BUSY:
+ reason = "STATUS_SHARED_IRQ_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FT_ORPHANING:
+ reason = "STATUS_FT_ORPHANING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT:
+ reason = "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PARTITION_FAILURE:
+ reason = "STATUS_PARTITION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_BLOCK_LENGTH:
+ reason = "STATUS_INVALID_BLOCK_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_PARTITIONED:
+ reason = "STATUS_DEVICE_NOT_PARTITIONED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNABLE_TO_LOCK_MEDIA:
+ reason = "STATUS_UNABLE_TO_LOCK_MEDIA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNABLE_TO_UNLOAD_MEDIA:
+ reason = "STATUS_UNABLE_TO_UNLOAD_MEDIA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EOM_OVERFLOW:
+ reason = "STATUS_EOM_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_MEDIA:
+ reason = "STATUS_NO_MEDIA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SUCH_MEMBER:
+ reason = "STATUS_NO_SUCH_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_MEMBER:
+ reason = "STATUS_INVALID_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KEY_DELETED:
+ reason = "STATUS_KEY_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_LOG_SPACE:
+ reason = "STATUS_NO_LOG_SPACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_SIDS:
+ reason = "STATUS_TOO_MANY_SIDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LM_CROSS_ENCRYPTION_REQUIRED:
+ reason = "STATUS_LM_CROSS_ENCRYPTION_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KEY_HAS_CHILDREN:
+ reason = "STATUS_KEY_HAS_CHILDREN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CHILD_MUST_BE_VOLATILE:
+ reason = "STATUS_CHILD_MUST_BE_VOLATILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_CONFIGURATION_ERROR:
+ reason = "STATUS_DEVICE_CONFIGURATION_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_INTERNAL_ERROR:
+ reason = "STATUS_DRIVER_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_STATE:
+ reason = "STATUS_INVALID_DEVICE_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_DEVICE_ERROR:
+ reason = "STATUS_IO_DEVICE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_PROTOCOL_ERROR:
+ reason = "STATUS_DEVICE_PROTOCOL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BACKUP_CONTROLLER:
+ reason = "STATUS_BACKUP_CONTROLLER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_FILE_FULL:
+ reason = "STATUS_LOG_FILE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_LATE:
+ reason = "STATUS_TOO_LATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_TRUST_LSA_SECRET:
+ reason = "STATUS_NO_TRUST_LSA_SECRET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_TRUST_SAM_ACCOUNT:
+ reason = "STATUS_NO_TRUST_SAM_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRUSTED_DOMAIN_FAILURE:
+ reason = "STATUS_TRUSTED_DOMAIN_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRUSTED_RELATIONSHIP_FAILURE:
+ reason = "STATUS_TRUSTED_RELATIONSHIP_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EVENTLOG_FILE_CORRUPT:
+ reason = "STATUS_EVENTLOG_FILE_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EVENTLOG_CANT_START:
+ reason = "STATUS_EVENTLOG_CANT_START";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRUST_FAILURE:
+ reason = "STATUS_TRUST_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUTANT_LIMIT_EXCEEDED:
+ reason = "STATUS_MUTANT_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETLOGON_NOT_STARTED:
+ reason = "STATUS_NETLOGON_NOT_STARTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCOUNT_EXPIRED:
+ reason = "STATUS_ACCOUNT_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_POSSIBLE_DEADLOCK:
+ reason = "STATUS_POSSIBLE_DEADLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_CREDENTIAL_CONFLICT:
+ reason = "STATUS_NETWORK_CREDENTIAL_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_SESSION_LIMIT:
+ reason = "STATUS_REMOTE_SESSION_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EVENTLOG_FILE_CHANGED:
+ reason = "STATUS_EVENTLOG_FILE_CHANGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT:
+ reason = "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
+ reason = "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT:
+ reason = "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOMAIN_TRUST_INCONSISTENT:
+ reason = "STATUS_DOMAIN_TRUST_INCONSISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FS_DRIVER_REQUIRED:
+ reason = "STATUS_FS_DRIVER_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_ALREADY_LOADED_AS_DLL:
+ reason = "STATUS_IMAGE_ALREADY_LOADED_AS_DLL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING:
+ reason = "STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME:
+ reason = "STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECURITY_STREAM_IS_INCONSISTENT:
+ reason = "STATUS_SECURITY_STREAM_IS_INCONSISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LOCK_RANGE:
+ reason = "STATUS_INVALID_LOCK_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ACE_CONDITION:
+ reason = "STATUS_INVALID_ACE_CONDITION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT:
+ reason = "STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOTIFICATION_GUID_ALREADY_DEFINED:
+ reason = "STATUS_NOTIFICATION_GUID_ALREADY_DEFINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_EXCEPTION_HANDLER:
+ reason = "STATUS_INVALID_EXCEPTION_HANDLER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DUPLICATE_PRIVILEGES:
+ reason = "STATUS_DUPLICATE_PRIVILEGES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_ALLOWED_ON_SYSTEM_FILE:
+ reason = "STATUS_NOT_ALLOWED_ON_SYSTEM_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REPAIR_NEEDED:
+ reason = "STATUS_REPAIR_NEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_QUOTA_NOT_ENABLED:
+ reason = "STATUS_QUOTA_NOT_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_APPLICATION_PACKAGE:
+ reason = "STATUS_NO_APPLICATION_PACKAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_OPEN_RESTRICTION:
+ reason = "STATUS_NETWORK_OPEN_RESTRICTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_USER_SESSION_KEY:
+ reason = "STATUS_NO_USER_SESSION_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_USER_SESSION_DELETED:
+ reason = "STATUS_USER_SESSION_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_LANG_NOT_FOUND:
+ reason = "STATUS_RESOURCE_LANG_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFF_SERVER_RESOURCES:
+ reason = "STATUS_INSUFF_SERVER_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_BUFFER_SIZE:
+ reason = "STATUS_INVALID_BUFFER_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS_COMPONENT:
+ reason = "STATUS_INVALID_ADDRESS_COMPONENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS_WILDCARD:
+ reason = "STATUS_INVALID_ADDRESS_WILDCARD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_ADDRESSES:
+ reason = "STATUS_TOO_MANY_ADDRESSES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADDRESS_ALREADY_EXISTS:
+ reason = "STATUS_ADDRESS_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADDRESS_CLOSED:
+ reason = "STATUS_ADDRESS_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_DISCONNECTED:
+ reason = "STATUS_CONNECTION_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_RESET:
+ reason = "STATUS_CONNECTION_RESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_NODES:
+ reason = "STATUS_TOO_MANY_NODES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_ABORTED:
+ reason = "STATUS_TRANSACTION_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_TIMED_OUT:
+ reason = "STATUS_TRANSACTION_TIMED_OUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_RELEASE:
+ reason = "STATUS_TRANSACTION_NO_RELEASE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_MATCH:
+ reason = "STATUS_TRANSACTION_NO_MATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_RESPONDED:
+ reason = "STATUS_TRANSACTION_RESPONDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_ID:
+ reason = "STATUS_TRANSACTION_INVALID_ID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_TYPE:
+ reason = "STATUS_TRANSACTION_INVALID_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SERVER_SESSION:
+ reason = "STATUS_NOT_SERVER_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_CLIENT_SESSION:
+ reason = "STATUS_NOT_CLIENT_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_LOAD_REGISTRY_FILE:
+ reason = "STATUS_CANNOT_LOAD_REGISTRY_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEBUG_ATTACH_FAILED:
+ reason = "STATUS_DEBUG_ATTACH_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_PROCESS_TERMINATED:
+ reason = "STATUS_SYSTEM_PROCESS_TERMINATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATA_NOT_ACCEPTED:
+ reason = "STATUS_DATA_NOT_ACCEPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_BROWSER_SERVERS_FOUND:
+ reason = "STATUS_NO_BROWSER_SERVERS_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VDM_HARD_ERROR:
+ reason = "STATUS_VDM_HARD_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_CANCEL_TIMEOUT:
+ reason = "STATUS_DRIVER_CANCEL_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REPLY_MESSAGE_MISMATCH:
+ reason = "STATUS_REPLY_MESSAGE_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MAPPED_ALIGNMENT:
+ reason = "STATUS_MAPPED_ALIGNMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_CHECKSUM_MISMATCH:
+ reason = "STATUS_IMAGE_CHECKSUM_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA:
+ reason = "STATUS_LOST_WRITEBEHIND_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLIENT_SERVER_PARAMETERS_INVALID:
+ reason = "STATUS_CLIENT_SERVER_PARAMETERS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PASSWORD_MUST_CHANGE:
+ reason = "STATUS_PASSWORD_MUST_CHANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_FOUND:
+ reason = "STATUS_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_TINY_STREAM:
+ reason = "STATUS_NOT_TINY_STREAM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RECOVERY_FAILURE:
+ reason = "STATUS_RECOVERY_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STACK_OVERFLOW_READ:
+ reason = "STATUS_STACK_OVERFLOW_READ";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FAIL_CHECK:
+ reason = "STATUS_FAIL_CHECK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DUPLICATE_OBJECTID:
+ reason = "STATUS_DUPLICATE_OBJECTID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECTID_EXISTS:
+ reason = "STATUS_OBJECTID_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONVERT_TO_LARGE:
+ reason = "STATUS_CONVERT_TO_LARGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RETRY:
+ reason = "STATUS_RETRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FOUND_OUT_OF_SCOPE:
+ reason = "STATUS_FOUND_OUT_OF_SCOPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALLOCATE_BUCKET:
+ reason = "STATUS_ALLOCATE_BUCKET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROPSET_NOT_FOUND:
+ reason = "STATUS_PROPSET_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MARSHALL_OVERFLOW:
+ reason = "STATUS_MARSHALL_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_VARIANT:
+ reason = "STATUS_INVALID_VARIANT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
+ reason = "STATUS_DOMAIN_CONTROLLER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCOUNT_LOCKED_OUT:
+ reason = "STATUS_ACCOUNT_LOCKED_OUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HANDLE_NOT_CLOSABLE:
+ reason = "STATUS_HANDLE_NOT_CLOSABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_REFUSED:
+ reason = "STATUS_CONNECTION_REFUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRACEFUL_DISCONNECT:
+ reason = "STATUS_GRACEFUL_DISCONNECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADDRESS_ALREADY_ASSOCIATED:
+ reason = "STATUS_ADDRESS_ALREADY_ASSOCIATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADDRESS_NOT_ASSOCIATED:
+ reason = "STATUS_ADDRESS_NOT_ASSOCIATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_INVALID:
+ reason = "STATUS_CONNECTION_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_ACTIVE:
+ reason = "STATUS_CONNECTION_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_UNREACHABLE:
+ reason = "STATUS_NETWORK_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HOST_UNREACHABLE:
+ reason = "STATUS_HOST_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROTOCOL_UNREACHABLE:
+ reason = "STATUS_PROTOCOL_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_UNREACHABLE:
+ reason = "STATUS_PORT_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REQUEST_ABORTED:
+ reason = "STATUS_REQUEST_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_ABORTED:
+ reason = "STATUS_CONNECTION_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_COMPRESSION_BUFFER:
+ reason = "STATUS_BAD_COMPRESSION_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_USER_MAPPED_FILE:
+ reason = "STATUS_USER_MAPPED_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUDIT_FAILED:
+ reason = "STATUS_AUDIT_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TIMER_RESOLUTION_NOT_SET:
+ reason = "STATUS_TIMER_RESOLUTION_NOT_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONNECTION_COUNT_LIMIT:
+ reason = "STATUS_CONNECTION_COUNT_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGIN_TIME_RESTRICTION:
+ reason = "STATUS_LOGIN_TIME_RESTRICTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOGIN_WKSTA_RESTRICTION:
+ reason = "STATUS_LOGIN_WKSTA_RESTRICTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_MP_UP_MISMATCH:
+ reason = "STATUS_IMAGE_MP_UP_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_LOGON_INFO:
+ reason = "STATUS_INSUFFICIENT_LOGON_INFO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_DLL_ENTRYPOINT:
+ reason = "STATUS_BAD_DLL_ENTRYPOINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_SERVICE_ENTRYPOINT:
+ reason = "STATUS_BAD_SERVICE_ENTRYPOINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LPC_REPLY_LOST:
+ reason = "STATUS_LPC_REPLY_LOST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IP_ADDRESS_CONFLICT1:
+ reason = "STATUS_IP_ADDRESS_CONFLICT1";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IP_ADDRESS_CONFLICT2:
+ reason = "STATUS_IP_ADDRESS_CONFLICT2";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REGISTRY_QUOTA_LIMIT:
+ reason = "STATUS_REGISTRY_QUOTA_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PATH_NOT_COVERED:
+ reason = "STATUS_PATH_NOT_COVERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_CALLBACK_ACTIVE:
+ reason = "STATUS_NO_CALLBACK_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LICENSE_QUOTA_EXCEEDED:
+ reason = "STATUS_LICENSE_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PWD_TOO_SHORT:
+ reason = "STATUS_PWD_TOO_SHORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PWD_TOO_RECENT:
+ reason = "STATUS_PWD_TOO_RECENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PWD_HISTORY_CONFLICT:
+ reason = "STATUS_PWD_HISTORY_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PLUGPLAY_NO_DEVICE:
+ reason = "STATUS_PLUGPLAY_NO_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNSUPPORTED_COMPRESSION:
+ reason = "STATUS_UNSUPPORTED_COMPRESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_HW_PROFILE:
+ reason = "STATUS_INVALID_HW_PROFILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PLUGPLAY_DEVICE_PATH:
+ reason = "STATUS_INVALID_PLUGPLAY_DEVICE_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_ORDINAL_NOT_FOUND:
+ reason = "STATUS_DRIVER_ORDINAL_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND:
+ reason = "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_NOT_OWNED:
+ reason = "STATUS_RESOURCE_NOT_OWNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_LINKS:
+ reason = "STATUS_TOO_MANY_LINKS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_QUOTA_LIST_INCONSISTENT:
+ reason = "STATUS_QUOTA_LIST_INCONSISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_IS_OFFLINE:
+ reason = "STATUS_FILE_IS_OFFLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EVALUATION_EXPIRATION:
+ reason = "STATUS_EVALUATION_EXPIRATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_DLL_RELOCATION:
+ reason = "STATUS_ILLEGAL_DLL_RELOCATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LICENSE_VIOLATION:
+ reason = "STATUS_LICENSE_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DLL_INIT_FAILED_LOGOFF:
+ reason = "STATUS_DLL_INIT_FAILED_LOGOFF";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_UNABLE_TO_LOAD:
+ reason = "STATUS_DRIVER_UNABLE_TO_LOAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DFS_UNAVAILABLE:
+ reason = "STATUS_DFS_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLUME_DISMOUNTED:
+ reason = "STATUS_VOLUME_DISMOUNTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WX86_INTERNAL_ERROR:
+ reason = "STATUS_WX86_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WX86_FLOAT_STACK_CHECK:
+ reason = "STATUS_WX86_FLOAT_STACK_CHECK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VALIDATE_CONTINUE:
+ reason = "STATUS_VALIDATE_CONTINUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_MATCH:
+ reason = "STATUS_NO_MATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_MORE_MATCHES:
+ reason = "STATUS_NO_MORE_MATCHES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_A_REPARSE_POINT:
+ reason = "STATUS_NOT_A_REPARSE_POINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_INVALID:
+ reason = "STATUS_IO_REPARSE_TAG_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_MISMATCH:
+ reason = "STATUS_IO_REPARSE_TAG_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_REPARSE_DATA_INVALID:
+ reason = "STATUS_IO_REPARSE_DATA_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_NOT_HANDLED:
+ reason = "STATUS_IO_REPARSE_TAG_NOT_HANDLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PWD_TOO_LONG:
+ reason = "STATUS_PWD_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STOWED_EXCEPTION:
+ reason = "STATUS_STOWED_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REPARSE_POINT_NOT_RESOLVED:
+ reason = "STATUS_REPARSE_POINT_NOT_RESOLVED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DIRECTORY_IS_A_REPARSE_POINT:
+ reason = "STATUS_DIRECTORY_IS_A_REPARSE_POINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RANGE_LIST_CONFLICT:
+ reason = "STATUS_RANGE_LIST_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SOURCE_ELEMENT_EMPTY:
+ reason = "STATUS_SOURCE_ELEMENT_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DESTINATION_ELEMENT_FULL:
+ reason = "STATUS_DESTINATION_ELEMENT_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ILLEGAL_ELEMENT_ADDRESS:
+ reason = "STATUS_ILLEGAL_ELEMENT_ADDRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MAGAZINE_NOT_PRESENT:
+ reason = "STATUS_MAGAZINE_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REINITIALIZATION_NEEDED:
+ reason = "STATUS_REINITIALIZATION_NEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENCRYPTION_FAILED:
+ reason = "STATUS_ENCRYPTION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DECRYPTION_FAILED:
+ reason = "STATUS_DECRYPTION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RANGE_NOT_FOUND:
+ reason = "STATUS_RANGE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_RECOVERY_POLICY:
+ reason = "STATUS_NO_RECOVERY_POLICY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_EFS:
+ reason = "STATUS_NO_EFS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_EFS:
+ reason = "STATUS_WRONG_EFS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_USER_KEYS:
+ reason = "STATUS_NO_USER_KEYS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_NOT_ENCRYPTED:
+ reason = "STATUS_FILE_NOT_ENCRYPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_EXPORT_FORMAT:
+ reason = "STATUS_NOT_EXPORT_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_ENCRYPTED:
+ reason = "STATUS_FILE_ENCRYPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_GUID_NOT_FOUND:
+ reason = "STATUS_WMI_GUID_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_INSTANCE_NOT_FOUND:
+ reason = "STATUS_WMI_INSTANCE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_ITEMID_NOT_FOUND:
+ reason = "STATUS_WMI_ITEMID_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_TRY_AGAIN:
+ reason = "STATUS_WMI_TRY_AGAIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHARED_POLICY:
+ reason = "STATUS_SHARED_POLICY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_POLICY_OBJECT_NOT_FOUND:
+ reason = "STATUS_POLICY_OBJECT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_POLICY_ONLY_IN_DS:
+ reason = "STATUS_POLICY_ONLY_IN_DS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLUME_NOT_UPGRADED:
+ reason = "STATUS_VOLUME_NOT_UPGRADED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_STORAGE_NOT_ACTIVE:
+ reason = "STATUS_REMOTE_STORAGE_NOT_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_STORAGE_MEDIA_ERROR:
+ reason = "STATUS_REMOTE_STORAGE_MEDIA_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_TRACKING_SERVICE:
+ reason = "STATUS_NO_TRACKING_SERVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERVER_SID_MISMATCH:
+ reason = "STATUS_SERVER_SID_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_ATTRIBUTE_OR_VALUE:
+ reason = "STATUS_DS_NO_ATTRIBUTE_OR_VALUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_INVALID_ATTRIBUTE_SYNTAX:
+ reason = "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED:
+ reason = "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS:
+ reason = "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_BUSY:
+ reason = "STATUS_DS_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_UNAVAILABLE:
+ reason = "STATUS_DS_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_RIDS_ALLOCATED:
+ reason = "STATUS_DS_NO_RIDS_ALLOCATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_MORE_RIDS:
+ reason = "STATUS_DS_NO_MORE_RIDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_INCORRECT_ROLE_OWNER:
+ reason = "STATUS_DS_INCORRECT_ROLE_OWNER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_RIDMGR_INIT_ERROR:
+ reason = "STATUS_DS_RIDMGR_INIT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_OBJ_CLASS_VIOLATION:
+ reason = "STATUS_DS_OBJ_CLASS_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CANT_ON_NON_LEAF:
+ reason = "STATUS_DS_CANT_ON_NON_LEAF";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CANT_ON_RDN:
+ reason = "STATUS_DS_CANT_ON_RDN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CANT_MOD_OBJ_CLASS:
+ reason = "STATUS_DS_CANT_MOD_OBJ_CLASS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CROSS_DOM_MOVE_FAILED:
+ reason = "STATUS_DS_CROSS_DOM_MOVE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GC_NOT_AVAILABLE:
+ reason = "STATUS_DS_GC_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DIRECTORY_SERVICE_REQUIRED:
+ reason = "STATUS_DIRECTORY_SERVICE_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REPARSE_ATTRIBUTE_CONFLICT:
+ reason = "STATUS_REPARSE_ATTRIBUTE_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_ENABLE_DENY_ONLY:
+ reason = "STATUS_CANT_ENABLE_DENY_ONLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_MULTIPLE_FAULTS:
+ reason = "STATUS_FLOAT_MULTIPLE_FAULTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOAT_MULTIPLE_TRAPS:
+ reason = "STATUS_FLOAT_MULTIPLE_TRAPS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_REMOVED:
+ reason = "STATUS_DEVICE_REMOVED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_JOURNAL_DELETE_IN_PROGRESS:
+ reason = "STATUS_JOURNAL_DELETE_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_JOURNAL_NOT_ACTIVE:
+ reason = "STATUS_JOURNAL_NOT_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOINTERFACE:
+ reason = "STATUS_NOINTERFACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_RIDMGR_DISABLED:
+ reason = "STATUS_DS_RIDMGR_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_ADMIN_LIMIT_EXCEEDED:
+ reason = "STATUS_DS_ADMIN_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_FAILED_SLEEP:
+ reason = "STATUS_DRIVER_FAILED_SLEEP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUTUAL_AUTHENTICATION_FAILED:
+ reason = "STATUS_MUTUAL_AUTHENTICATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_SYSTEM_FILE:
+ reason = "STATUS_CORRUPT_SYSTEM_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATATYPE_MISALIGNMENT_ERROR:
+ reason = "STATUS_DATATYPE_MISALIGNMENT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_READ_ONLY:
+ reason = "STATUS_WMI_READ_ONLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_SET_FAILURE:
+ reason = "STATUS_WMI_SET_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COMMITMENT_MINIMUM:
+ reason = "STATUS_COMMITMENT_MINIMUM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REG_NAT_CONSUMPTION:
+ reason = "STATUS_REG_NAT_CONSUMPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSPORT_FULL:
+ reason = "STATUS_TRANSPORT_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_SAM_INIT_FAILURE:
+ reason = "STATUS_DS_SAM_INIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ONLY_IF_CONNECTED:
+ reason = "STATUS_ONLY_IF_CONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_SENSITIVE_GROUP_VIOLATION:
+ reason = "STATUS_DS_SENSITIVE_GROUP_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_RESTART_ENUMERATION:
+ reason = "STATUS_PNP_RESTART_ENUMERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_JOURNAL_ENTRY_DELETED:
+ reason = "STATUS_JOURNAL_ENTRY_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CANT_MOD_PRIMARYGROUPID:
+ reason = "STATUS_DS_CANT_MOD_PRIMARYGROUPID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
+ reason = "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_REBOOT_REQUIRED:
+ reason = "STATUS_PNP_REBOOT_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_POWER_STATE_INVALID:
+ reason = "STATUS_POWER_STATE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_INVALID_GROUP_TYPE:
+ reason = "STATUS_DS_INVALID_GROUP_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN:
+ reason = "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN:
+ reason = "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER:
+ reason = "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER:
+ reason = "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER:
+ reason = "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER:
+ reason = "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER:
+ reason = "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_HAVE_PRIMARY_MEMBERS:
+ reason = "STATUS_DS_HAVE_PRIMARY_MEMBERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_NOT_SUPPORTED:
+ reason = "STATUS_WMI_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_POWER:
+ reason = "STATUS_INSUFFICIENT_POWER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SAM_NEED_BOOTKEY_PASSWORD:
+ reason = "STATUS_SAM_NEED_BOOTKEY_PASSWORD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SAM_NEED_BOOTKEY_FLOPPY:
+ reason = "STATUS_SAM_NEED_BOOTKEY_FLOPPY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_CANT_START:
+ reason = "STATUS_DS_CANT_START";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_INIT_FAILURE:
+ reason = "STATUS_DS_INIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SAM_INIT_FAILURE:
+ reason = "STATUS_SAM_INIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GC_REQUIRED:
+ reason = "STATUS_DS_GC_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY:
+ reason = "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS:
+ reason = "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED:
+ reason = "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MULTIPLE_FAULT_VIOLATION:
+ reason = "STATUS_MULTIPLE_FAULT_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CURRENT_DOMAIN_NOT_ALLOWED:
+ reason = "STATUS_CURRENT_DOMAIN_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_MAKE:
+ reason = "STATUS_CANNOT_MAKE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_SHUTDOWN:
+ reason = "STATUS_SYSTEM_SHUTDOWN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_INIT_FAILURE_CONSOLE:
+ reason = "STATUS_DS_INIT_FAILURE_CONSOLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_SAM_INIT_FAILURE_CONSOLE:
+ reason = "STATUS_DS_SAM_INIT_FAILURE_CONSOLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNFINISHED_CONTEXT_DELETED:
+ reason = "STATUS_UNFINISHED_CONTEXT_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_TGT_REPLY:
+ reason = "STATUS_NO_TGT_REPLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECTID_NOT_FOUND:
+ reason = "STATUS_OBJECTID_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_IP_ADDRESSES:
+ reason = "STATUS_NO_IP_ADDRESSES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_CREDENTIAL_HANDLE:
+ reason = "STATUS_WRONG_CREDENTIAL_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CRYPTO_SYSTEM_INVALID:
+ reason = "STATUS_CRYPTO_SYSTEM_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MAX_REFERRALS_EXCEEDED:
+ reason = "STATUS_MAX_REFERRALS_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUST_BE_KDC:
+ reason = "STATUS_MUST_BE_KDC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STRONG_CRYPTO_NOT_SUPPORTED:
+ reason = "STATUS_STRONG_CRYPTO_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_PRINCIPALS:
+ reason = "STATUS_TOO_MANY_PRINCIPALS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_PA_DATA:
+ reason = "STATUS_NO_PA_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PKINIT_NAME_MISMATCH:
+ reason = "STATUS_PKINIT_NAME_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_LOGON_REQUIRED:
+ reason = "STATUS_SMARTCARD_LOGON_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KDC_INVALID_REQUEST:
+ reason = "STATUS_KDC_INVALID_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KDC_UNABLE_TO_REFER:
+ reason = "STATUS_KDC_UNABLE_TO_REFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KDC_UNKNOWN_ETYPE:
+ reason = "STATUS_KDC_UNKNOWN_ETYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHUTDOWN_IN_PROGRESS:
+ reason = "STATUS_SHUTDOWN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERVER_SHUTDOWN_IN_PROGRESS:
+ reason = "STATUS_SERVER_SHUTDOWN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED_ON_SBS:
+ reason = "STATUS_NOT_SUPPORTED_ON_SBS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_GUID_DISCONNECTED:
+ reason = "STATUS_WMI_GUID_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_ALREADY_DISABLED:
+ reason = "STATUS_WMI_ALREADY_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WMI_ALREADY_ENABLED:
+ reason = "STATUS_WMI_ALREADY_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MFT_TOO_FRAGMENTED:
+ reason = "STATUS_MFT_TOO_FRAGMENTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COPY_PROTECTION_FAILURE:
+ reason = "STATUS_COPY_PROTECTION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_AUTHENTICATION_FAILURE:
+ reason = "STATUS_CSS_AUTHENTICATION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_KEY_NOT_PRESENT:
+ reason = "STATUS_CSS_KEY_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_KEY_NOT_ESTABLISHED:
+ reason = "STATUS_CSS_KEY_NOT_ESTABLISHED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_SCRAMBLED_SECTOR:
+ reason = "STATUS_CSS_SCRAMBLED_SECTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_REGION_MISMATCH:
+ reason = "STATUS_CSS_REGION_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CSS_RESETS_EXHAUSTED:
+ reason = "STATUS_CSS_RESETS_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PASSWORD_CHANGE_REQUIRED:
+ reason = "STATUS_PASSWORD_CHANGE_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PKINIT_FAILURE:
+ reason = "STATUS_PKINIT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
+ reason = "STATUS_SMARTCARD_SUBSYSTEM_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_KERB_KEY:
+ reason = "STATUS_NO_KERB_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HOST_DOWN:
+ reason = "STATUS_HOST_DOWN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNSUPPORTED_PREAUTH:
+ reason = "STATUS_UNSUPPORTED_PREAUTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EFS_ALG_BLOB_TOO_BIG:
+ reason = "STATUS_EFS_ALG_BLOB_TOO_BIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_NOT_SET:
+ reason = "STATUS_PORT_NOT_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEBUGGER_INACTIVE:
+ reason = "STATUS_DEBUGGER_INACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_VERSION_CHECK_FAILURE:
+ reason = "STATUS_DS_VERSION_CHECK_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUDITING_DISABLED:
+ reason = "STATUS_AUDITING_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PRENT4_MACHINE_ACCOUNT:
+ reason = "STATUS_PRENT4_MACHINE_ACCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER:
+ reason = "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_32:
+ reason = "STATUS_INVALID_IMAGE_WIN_32";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_64:
+ reason = "STATUS_INVALID_IMAGE_WIN_64";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_BINDINGS:
+ reason = "STATUS_BAD_BINDINGS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NETWORK_SESSION_EXPIRED:
+ reason = "STATUS_NETWORK_SESSION_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APPHELP_BLOCK:
+ reason = "STATUS_APPHELP_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALL_SIDS_FILTERED:
+ reason = "STATUS_ALL_SIDS_FILTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SAFE_MODE_DRIVER:
+ reason = "STATUS_NOT_SAFE_MODE_DRIVER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT:
+ reason = "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_PATH:
+ reason = "STATUS_ACCESS_DISABLED_BY_POLICY_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER:
+ reason = "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER:
+ reason = "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FAILED_DRIVER_ENTRY:
+ reason = "STATUS_FAILED_DRIVER_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_ENUMERATION_ERROR:
+ reason = "STATUS_DEVICE_ENUMERATION_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MOUNT_POINT_NOT_RESOLVED:
+ reason = "STATUS_MOUNT_POINT_NOT_RESOLVED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_OBJECT_PARAMETER:
+ reason = "STATUS_INVALID_DEVICE_OBJECT_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MCA_OCCURED:
+ reason = "STATUS_MCA_OCCURED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_BLOCKED_CRITICAL:
+ reason = "STATUS_DRIVER_BLOCKED_CRITICAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_BLOCKED:
+ reason = "STATUS_DRIVER_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_DATABASE_ERROR:
+ reason = "STATUS_DRIVER_DATABASE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_HIVE_TOO_LARGE:
+ reason = "STATUS_SYSTEM_HIVE_TOO_LARGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMPORT_OF_NON_DLL:
+ reason = "STATUS_INVALID_IMPORT_OF_NON_DLL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SECRETS:
+ reason = "STATUS_NO_SECRETS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY:
+ reason = "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FAILED_STACK_SWITCH:
+ reason = "STATUS_FAILED_STACK_SWITCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HEAP_CORRUPTION:
+ reason = "STATUS_HEAP_CORRUPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_WRONG_PIN:
+ reason = "STATUS_SMARTCARD_WRONG_PIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_CARD_BLOCKED:
+ reason = "STATUS_SMARTCARD_CARD_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED:
+ reason = "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_CARD:
+ reason = "STATUS_SMARTCARD_NO_CARD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_KEY_CONTAINER:
+ reason = "STATUS_SMARTCARD_NO_KEY_CONTAINER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_CERTIFICATE:
+ reason = "STATUS_SMARTCARD_NO_CERTIFICATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_KEYSET:
+ reason = "STATUS_SMARTCARD_NO_KEYSET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_IO_ERROR:
+ reason = "STATUS_SMARTCARD_IO_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DOWNGRADE_DETECTED:
+ reason = "STATUS_DOWNGRADE_DETECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_CERT_REVOKED:
+ reason = "STATUS_SMARTCARD_CERT_REVOKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ISSUING_CA_UNTRUSTED:
+ reason = "STATUS_ISSUING_CA_UNTRUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REVOCATION_OFFLINE_C:
+ reason = "STATUS_REVOCATION_OFFLINE_C";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PKINIT_CLIENT_FAILURE:
+ reason = "STATUS_PKINIT_CLIENT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_CERT_EXPIRED:
+ reason = "STATUS_SMARTCARD_CERT_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_FAILED_PRIOR_UNLOAD:
+ reason = "STATUS_DRIVER_FAILED_PRIOR_UNLOAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMARTCARD_SILENT_CONTEXT:
+ reason = "STATUS_SMARTCARD_SILENT_CONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PER_USER_TRUST_QUOTA_EXCEEDED:
+ reason = "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED:
+ reason = "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED:
+ reason = "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_NAME_NOT_UNIQUE:
+ reason = "STATUS_DS_NAME_NOT_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_DUPLICATE_ID_FOUND:
+ reason = "STATUS_DS_DUPLICATE_ID_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_GROUP_CONVERSION_ERROR:
+ reason = "STATUS_DS_GROUP_CONVERSION_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLSNAP_PREPARE_HIBERNATE:
+ reason = "STATUS_VOLSNAP_PREPARE_HIBERNATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_USER2USER_REQUIRED:
+ reason = "STATUS_USER2USER_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STACK_BUFFER_OVERRUN:
+ reason = "STATUS_STACK_BUFFER_OVERRUN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_S4U_PROT_SUPPORT:
+ reason = "STATUS_NO_S4U_PROT_SUPPORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CROSSREALM_DELEGATION_FAILURE:
+ reason = "STATUS_CROSSREALM_DELEGATION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REVOCATION_OFFLINE_KDC:
+ reason = "STATUS_REVOCATION_OFFLINE_KDC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ISSUING_CA_UNTRUSTED_KDC:
+ reason = "STATUS_ISSUING_CA_UNTRUSTED_KDC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KDC_CERT_EXPIRED:
+ reason = "STATUS_KDC_CERT_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_KDC_CERT_REVOKED:
+ reason = "STATUS_KDC_CERT_REVOKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PARAMETER_QUOTA_EXCEEDED:
+ reason = "STATUS_PARAMETER_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HIBERNATION_FAILURE:
+ reason = "STATUS_HIBERNATION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DELAY_LOAD_FAILED:
+ reason = "STATUS_DELAY_LOAD_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUTHENTICATION_FIREWALL_FAILED:
+ reason = "STATUS_AUTHENTICATION_FIREWALL_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VDM_DISALLOWED:
+ reason = "STATUS_VDM_DISALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HUNG_DISPLAY_DRIVER_THREAD:
+ reason = "STATUS_HUNG_DISPLAY_DRIVER_THREAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE:
+ reason = "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_CRUNTIME_PARAMETER:
+ reason = "STATUS_INVALID_CRUNTIME_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NTLM_BLOCKED:
+ reason = "STATUS_NTLM_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_SRC_SID_EXISTS_IN_FOREST:
+ reason = "STATUS_DS_SRC_SID_EXISTS_IN_FOREST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST:
+ reason = "STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST:
+ reason = "STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_USER_PRINCIPAL_NAME:
+ reason = "STATUS_INVALID_USER_PRINCIPAL_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FATAL_USER_CALLBACK_EXCEPTION:
+ reason = "STATUS_FATAL_USER_CALLBACK_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ASSERTION_FAILURE:
+ reason = "STATUS_ASSERTION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VERIFIER_STOP:
+ reason = "STATUS_VERIFIER_STOP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_POP_STACK:
+ reason = "STATUS_CALLBACK_POP_STACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_DRIVER_BLOCKED:
+ reason = "STATUS_INCOMPATIBLE_DRIVER_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HIVE_UNLOADED:
+ reason = "STATUS_HIVE_UNLOADED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COMPRESSION_DISABLED:
+ reason = "STATUS_COMPRESSION_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_SYSTEM_LIMITATION:
+ reason = "STATUS_FILE_SYSTEM_LIMITATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_HASH:
+ reason = "STATUS_INVALID_IMAGE_HASH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_CAPABLE:
+ reason = "STATUS_NOT_CAPABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REQUEST_OUT_OF_SEQUENCE:
+ reason = "STATUS_REQUEST_OUT_OF_SEQUENCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMPLEMENTATION_LIMIT:
+ reason = "STATUS_IMPLEMENTATION_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ELEVATION_REQUIRED:
+ reason = "STATUS_ELEVATION_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SECURITY_CONTEXT:
+ reason = "STATUS_NO_SECURITY_CONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PKU2U_CERT_FAILURE:
+ reason = "STATUS_PKU2U_CERT_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BEYOND_VDL:
+ reason = "STATUS_BEYOND_VDL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENCOUNTERED_WRITE_IN_PROGRESS:
+ reason = "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PTE_CHANGED:
+ reason = "STATUS_PTE_CHANGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PURGE_FAILED:
+ reason = "STATUS_PURGE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CRED_REQUIRES_CONFIRMATION:
+ reason = "STATUS_CRED_REQUIRES_CONFIRMATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE:
+ reason = "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER:
+ reason = "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE:
+ reason = "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE:
+ reason = "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_FILE_NOT_CSE:
+ reason = "STATUS_CS_ENCRYPTION_FILE_NOT_CSE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_LABEL:
+ reason = "STATUS_INVALID_LABEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DRIVER_PROCESS_TERMINATED:
+ reason = "STATUS_DRIVER_PROCESS_TERMINATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AMBIGUOUS_SYSTEM_DEVICE:
+ reason = "STATUS_AMBIGUOUS_SYSTEM_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_DEVICE_NOT_FOUND:
+ reason = "STATUS_SYSTEM_DEVICE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESTART_BOOT_APPLICATION:
+ reason = "STATUS_RESTART_BOOT_APPLICATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_NVRAM_RESOURCES:
+ reason = "STATUS_INSUFFICIENT_NVRAM_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SESSION:
+ reason = "STATUS_INVALID_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREAD_ALREADY_IN_SESSION:
+ reason = "STATUS_THREAD_ALREADY_IN_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREAD_NOT_IN_SESSION:
+ reason = "STATUS_THREAD_NOT_IN_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_WEIGHT:
+ reason = "STATUS_INVALID_WEIGHT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REQUEST_PAUSED:
+ reason = "STATUS_REQUEST_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_RANGES_PROCESSED:
+ reason = "STATUS_NO_RANGES_PROCESSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_RESOURCES_EXHAUSTED:
+ reason = "STATUS_DISK_RESOURCES_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NEEDS_REMEDIATION:
+ reason = "STATUS_NEEDS_REMEDIATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_FEATURE_NOT_SUPPORTED:
+ reason = "STATUS_DEVICE_FEATURE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_UNREACHABLE:
+ reason = "STATUS_DEVICE_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_TOKEN:
+ reason = "STATUS_INVALID_TOKEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SERVER_UNAVAILABLE:
+ reason = "STATUS_SERVER_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_NOT_AVAILABLE:
+ reason = "STATUS_FILE_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_INSUFFICIENT_RESOURCES:
+ reason = "STATUS_DEVICE_INSUFFICIENT_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PACKAGE_UPDATING:
+ reason = "STATUS_PACKAGE_UPDATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_READ_FROM_COPY:
+ reason = "STATUS_NOT_READ_FROM_COPY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FT_WRITE_FAILURE:
+ reason = "STATUS_FT_WRITE_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FT_DI_SCAN_REQUIRED:
+ reason = "STATUS_FT_DI_SCAN_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_NOT_EXTERNALLY_BACKED:
+ reason = "STATUS_OBJECT_NOT_EXTERNALLY_BACKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EXTERNAL_BACKING_PROVIDER_UNKNOWN:
+ reason = "STATUS_EXTERNAL_BACKING_PROVIDER_UNKNOWN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DATA_CHECKSUM_ERROR:
+ reason = "STATUS_DATA_CHECKSUM_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INTERMIXED_KERNEL_EA_OPERATION:
+ reason = "STATUS_INTERMIXED_KERNEL_EA_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRIM_READ_ZERO_NOT_SUPPORTED:
+ reason = "STATUS_TRIM_READ_ZERO_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TOO_MANY_SEGMENT_DESCRIPTORS:
+ reason = "STATUS_TOO_MANY_SEGMENT_DESCRIPTORS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_OFFSET_ALIGNMENT:
+ reason = "STATUS_INVALID_OFFSET_ALIGNMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_FIELD_IN_PARAMETER_LIST:
+ reason = "STATUS_INVALID_FIELD_IN_PARAMETER_LIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OPERATION_IN_PROGRESS:
+ reason = "STATUS_OPERATION_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_INITIATOR_TARGET_PATH:
+ reason = "STATUS_INVALID_INITIATOR_TARGET_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SCRUB_DATA_DISABLED:
+ reason = "STATUS_SCRUB_DATA_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_REDUNDANT_STORAGE:
+ reason = "STATUS_NOT_REDUNDANT_STORAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESIDENT_FILE_NOT_SUPPORTED:
+ reason = "STATUS_RESIDENT_FILE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COMPRESSED_FILE_NOT_SUPPORTED:
+ reason = "STATUS_COMPRESSED_FILE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_SUPPORTED:
+ reason = "STATUS_DIRECTORY_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_OPERATION_TIMEOUT:
+ reason = "STATUS_IO_OPERATION_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYSTEM_NEEDS_REMEDIATION:
+ reason = "STATUS_SYSTEM_NEEDS_REMEDIATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APPX_INTEGRITY_FAILURE_CLR_NGEN:
+ reason = "STATUS_APPX_INTEGRITY_FAILURE_CLR_NGEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SHARE_UNAVAILABLE:
+ reason = "STATUS_SHARE_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APISET_NOT_HOSTED:
+ reason = "STATUS_APISET_NOT_HOSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APISET_NOT_PRESENT:
+ reason = "STATUS_APISET_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DEVICE_HARDWARE_ERROR:
+ reason = "STATUS_DEVICE_HARDWARE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_TASK_NAME:
+ reason = "STATUS_INVALID_TASK_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_TASK_INDEX:
+ reason = "STATUS_INVALID_TASK_INDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREAD_ALREADY_IN_TASK:
+ reason = "STATUS_THREAD_ALREADY_IN_TASK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_BYPASS:
+ reason = "STATUS_CALLBACK_BYPASS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_UNDEFINED_SCOPE:
+ reason = "STATUS_UNDEFINED_SCOPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_CAP:
+ reason = "STATUS_INVALID_CAP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_GUI_PROCESS:
+ reason = "STATUS_NOT_GUI_PROCESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FAIL_FAST_EXCEPTION:
+ reason = "STATUS_FAIL_FAST_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IMAGE_CERT_REVOKED:
+ reason = "STATUS_IMAGE_CERT_REVOKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DYNAMIC_CODE_BLOCKED:
+ reason = "STATUS_DYNAMIC_CODE_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_CLOSED:
+ reason = "STATUS_PORT_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MESSAGE_LOST:
+ reason = "STATUS_MESSAGE_LOST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_MESSAGE:
+ reason = "STATUS_INVALID_MESSAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REQUEST_CANCELED:
+ reason = "STATUS_REQUEST_CANCELED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RECURSIVE_DISPATCH:
+ reason = "STATUS_RECURSIVE_DISPATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LPC_RECEIVE_BUFFER_EXPECTED:
+ reason = "STATUS_LPC_RECEIVE_BUFFER_EXPECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LPC_INVALID_CONNECTION_USAGE:
+ reason = "STATUS_LPC_INVALID_CONNECTION_USAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LPC_REQUESTS_NOT_ALLOWED:
+ reason = "STATUS_LPC_REQUESTS_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_IN_USE:
+ reason = "STATUS_RESOURCE_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HARDWARE_MEMORY_ERROR:
+ reason = "STATUS_HARDWARE_MEMORY_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_HANDLE_EXCEPTION:
+ reason = "STATUS_THREADPOOL_HANDLE_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED:
+ reason = "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED:
+ reason = "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED:
+ reason = "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED:
+ reason = "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASED_DURING_OPERATION:
+ reason = "STATUS_THREADPOOL_RELEASED_DURING_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING:
+ reason = "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APC_RETURNED_WHILE_IMPERSONATING:
+ reason = "STATUS_APC_RETURNED_WHILE_IMPERSONATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROCESS_IS_PROTECTED:
+ reason = "STATUS_PROCESS_IS_PROTECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MCA_EXCEPTION:
+ reason = "STATUS_MCA_EXCEPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE:
+ reason = "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SYMLINK_CLASS_DISABLED:
+ reason = "STATUS_SYMLINK_CLASS_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_IDN_NORMALIZATION:
+ reason = "STATUS_INVALID_IDN_NORMALIZATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_UNICODE_TRANSLATION:
+ reason = "STATUS_NO_UNICODE_TRANSLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ALREADY_REGISTERED:
+ reason = "STATUS_ALREADY_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONTEXT_MISMATCH:
+ reason = "STATUS_CONTEXT_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PORT_ALREADY_HAS_COMPLETION_LIST:
+ reason = "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_THREAD_PRIORITY:
+ reason = "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_THREAD:
+ reason = "STATUS_INVALID_THREAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_TRANSACTION:
+ reason = "STATUS_CALLBACK_RETURNED_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_LDR_LOCK:
+ reason = "STATUS_CALLBACK_RETURNED_LDR_LOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_LANG:
+ reason = "STATUS_CALLBACK_RETURNED_LANG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_PRI_BACK:
+ reason = "STATUS_CALLBACK_RETURNED_PRI_BACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_THREAD_AFFINITY:
+ reason = "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_REPAIR_DISABLED:
+ reason = "STATUS_DISK_REPAIR_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_DOMAIN_RENAME_IN_PROGRESS:
+ reason = "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_QUOTA_EXCEEDED:
+ reason = "STATUS_DISK_QUOTA_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CONTENT_BLOCKED:
+ reason = "STATUS_CONTENT_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_CLUSTERS:
+ reason = "STATUS_BAD_CLUSTERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLUME_DIRTY:
+ reason = "STATUS_VOLUME_DIRTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DISK_REPAIR_UNSUCCESSFUL:
+ reason = "STATUS_DISK_REPAIR_UNSUCCESSFUL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_OVERFULL:
+ reason = "STATUS_CORRUPT_LOG_OVERFULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_CORRUPTED:
+ reason = "STATUS_CORRUPT_LOG_CORRUPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_UNAVAILABLE:
+ reason = "STATUS_CORRUPT_LOG_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_DELETED_FULL:
+ reason = "STATUS_CORRUPT_LOG_DELETED_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_CLEARED:
+ reason = "STATUS_CORRUPT_LOG_CLEARED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ORPHAN_NAME_EXHAUSTED:
+ reason = "STATUS_ORPHAN_NAME_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROACTIVE_SCAN_IN_PROGRESS:
+ reason = "STATUS_PROACTIVE_SCAN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENCRYPTED_IO_NOT_POSSIBLE:
+ reason = "STATUS_ENCRYPTED_IO_NOT_POSSIBLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_UPLEVEL_RECORDS:
+ reason = "STATUS_CORRUPT_LOG_UPLEVEL_RECORDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_CHECKED_OUT:
+ reason = "STATUS_FILE_CHECKED_OUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CHECKOUT_REQUIRED:
+ reason = "STATUS_CHECKOUT_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_FILE_TYPE:
+ reason = "STATUS_BAD_FILE_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_TOO_LARGE:
+ reason = "STATUS_FILE_TOO_LARGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FORMS_AUTH_REQUIRED:
+ reason = "STATUS_FORMS_AUTH_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRUS_INFECTED:
+ reason = "STATUS_VIRUS_INFECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRUS_DELETED:
+ reason = "STATUS_VIRUS_DELETED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_MCFG_TABLE:
+ reason = "STATUS_BAD_MCFG_TABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_BREAK_OPLOCK:
+ reason = "STATUS_CANNOT_BREAK_OPLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_KEY:
+ reason = "STATUS_BAD_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BAD_DATA:
+ reason = "STATUS_BAD_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_KEY:
+ reason = "STATUS_NO_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_HANDLE_REVOKED:
+ reason = "STATUS_FILE_HANDLE_REVOKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WOW_ASSERTION:
+ reason = "STATUS_WOW_ASSERTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_SIGNATURE:
+ reason = "STATUS_INVALID_SIGNATURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HMAC_NOT_SUPPORTED:
+ reason = "STATUS_HMAC_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUTH_TAG_MISMATCH:
+ reason = "STATUS_AUTH_TAG_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_STATE_TRANSITION:
+ reason = "STATUS_INVALID_STATE_TRANSITION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_KERNEL_INFO_VERSION:
+ reason = "STATUS_INVALID_KERNEL_INFO_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PEP_INFO_VERSION:
+ reason = "STATUS_INVALID_PEP_INFO_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_QUEUE_OVERFLOW:
+ reason = "STATUS_IPSEC_QUEUE_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ND_QUEUE_OVERFLOW:
+ reason = "STATUS_ND_QUEUE_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HOPLIMIT_EXCEEDED:
+ reason = "STATUS_HOPLIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PROTOCOL_NOT_SUPPORTED:
+ reason = "STATUS_PROTOCOL_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FASTPATH_REJECTED:
+ reason = "STATUS_FASTPATH_REJECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED:
+ reason = "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR:
+ reason = "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR:
+ reason = "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_XML_PARSE_ERROR:
+ reason = "STATUS_XML_PARSE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_XMLDSIG_ERROR:
+ reason = "STATUS_XMLDSIG_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_WRONG_COMPARTMENT:
+ reason = "STATUS_WRONG_COMPARTMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUTHIP_FAILURE:
+ reason = "STATUS_AUTHIP_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS:
+ reason = "STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DS_OID_NOT_FOUND:
+ reason = "STATUS_DS_OID_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INCORRECT_ACCOUNT_TYPE:
+ reason = "STATUS_INCORRECT_ACCOUNT_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HASH_NOT_SUPPORTED:
+ reason = "STATUS_HASH_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HASH_NOT_PRESENT:
+ reason = "STATUS_HASH_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECONDARY_IC_PROVIDER_NOT_REGISTERED:
+ reason = "STATUS_SECONDARY_IC_PROVIDER_NOT_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GPIO_CLIENT_INFORMATION_INVALID:
+ reason = "STATUS_GPIO_CLIENT_INFORMATION_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GPIO_VERSION_NOT_SUPPORTED:
+ reason = "STATUS_GPIO_VERSION_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GPIO_INVALID_REGISTRATION_PACKET:
+ reason = "STATUS_GPIO_INVALID_REGISTRATION_PACKET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GPIO_OPERATION_DENIED:
+ reason = "STATUS_GPIO_OPERATION_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE:
+ reason = "STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_SWITCH_RUNLEVEL:
+ reason = "STATUS_CANNOT_SWITCH_RUNLEVEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_RUNLEVEL_SETTING:
+ reason = "STATUS_INVALID_RUNLEVEL_SETTING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_TIMEOUT:
+ reason = "STATUS_RUNLEVEL_SWITCH_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_AGENT_TIMEOUT:
+ reason = "STATUS_RUNLEVEL_SWITCH_AGENT_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_IN_PROGRESS:
+ reason = "STATUS_RUNLEVEL_SWITCH_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_APPCONTAINER:
+ reason = "STATUS_NOT_APPCONTAINER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED_IN_APPCONTAINER:
+ reason = "STATUS_NOT_SUPPORTED_IN_APPCONTAINER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_PACKAGE_SID_LENGTH:
+ reason = "STATUS_INVALID_PACKAGE_SID_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_DATA_NOT_FOUND:
+ reason = "STATUS_APP_DATA_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_DATA_EXPIRED:
+ reason = "STATUS_APP_DATA_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_DATA_CORRUPT:
+ reason = "STATUS_APP_DATA_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_DATA_LIMIT_EXCEEDED:
+ reason = "STATUS_APP_DATA_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_APP_DATA_REBOOT_REQUIRED:
+ reason = "STATUS_APP_DATA_REBOOT_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED:
+ reason = "STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OFFLOAD_WRITE_FLT_NOT_SUPPORTED:
+ reason = "STATUS_OFFLOAD_WRITE_FLT_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED:
+ reason = "STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED:
+ reason = "STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_DBG_NO_STATE_CHANGE:
+ reason = "DBG_NO_STATE_CHANGE";
+ break;
+ case MD_NTSTATUS_WIN_DBG_APP_NOT_IDLE:
+ reason = "DBG_APP_NOT_IDLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_STRING_BINDING:
+ reason = "RPC_NT_INVALID_STRING_BINDING";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_WRONG_KIND_OF_BINDING:
+ reason = "RPC_NT_WRONG_KIND_OF_BINDING";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_BINDING:
+ reason = "RPC_NT_INVALID_BINDING";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PROTSEQ_NOT_SUPPORTED:
+ reason = "RPC_NT_PROTSEQ_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_RPC_PROTSEQ:
+ reason = "RPC_NT_INVALID_RPC_PROTSEQ";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_STRING_UUID:
+ reason = "RPC_NT_INVALID_STRING_UUID";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_ENDPOINT_FORMAT:
+ reason = "RPC_NT_INVALID_ENDPOINT_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_NET_ADDR:
+ reason = "RPC_NT_INVALID_NET_ADDR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_ENDPOINT_FOUND:
+ reason = "RPC_NT_NO_ENDPOINT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_TIMEOUT:
+ reason = "RPC_NT_INVALID_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_OBJECT_NOT_FOUND:
+ reason = "RPC_NT_OBJECT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ALREADY_REGISTERED:
+ reason = "RPC_NT_ALREADY_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_TYPE_ALREADY_REGISTERED:
+ reason = "RPC_NT_TYPE_ALREADY_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ALREADY_LISTENING:
+ reason = "RPC_NT_ALREADY_LISTENING";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_PROTSEQS_REGISTERED:
+ reason = "RPC_NT_NO_PROTSEQS_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NOT_LISTENING:
+ reason = "RPC_NT_NOT_LISTENING";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_MGR_TYPE:
+ reason = "RPC_NT_UNKNOWN_MGR_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_IF:
+ reason = "RPC_NT_UNKNOWN_IF";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_BINDINGS:
+ reason = "RPC_NT_NO_BINDINGS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_PROTSEQS:
+ reason = "RPC_NT_NO_PROTSEQS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CANT_CREATE_ENDPOINT:
+ reason = "RPC_NT_CANT_CREATE_ENDPOINT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_OUT_OF_RESOURCES:
+ reason = "RPC_NT_OUT_OF_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SERVER_UNAVAILABLE:
+ reason = "RPC_NT_SERVER_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SERVER_TOO_BUSY:
+ reason = "RPC_NT_SERVER_TOO_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_NETWORK_OPTIONS:
+ reason = "RPC_NT_INVALID_NETWORK_OPTIONS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_CALL_ACTIVE:
+ reason = "RPC_NT_NO_CALL_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CALL_FAILED:
+ reason = "RPC_NT_CALL_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CALL_FAILED_DNE:
+ reason = "RPC_NT_CALL_FAILED_DNE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PROTOCOL_ERROR:
+ reason = "RPC_NT_PROTOCOL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_TRANS_SYN:
+ reason = "RPC_NT_UNSUPPORTED_TRANS_SYN";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_TYPE:
+ reason = "RPC_NT_UNSUPPORTED_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_TAG:
+ reason = "RPC_NT_INVALID_TAG";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_BOUND:
+ reason = "RPC_NT_INVALID_BOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_ENTRY_NAME:
+ reason = "RPC_NT_NO_ENTRY_NAME";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_NAME_SYNTAX:
+ reason = "RPC_NT_INVALID_NAME_SYNTAX";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_NAME_SYNTAX:
+ reason = "RPC_NT_UNSUPPORTED_NAME_SYNTAX";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UUID_NO_ADDRESS:
+ reason = "RPC_NT_UUID_NO_ADDRESS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_DUPLICATE_ENDPOINT:
+ reason = "RPC_NT_DUPLICATE_ENDPOINT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_TYPE:
+ reason = "RPC_NT_UNKNOWN_AUTHN_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_MAX_CALLS_TOO_SMALL:
+ reason = "RPC_NT_MAX_CALLS_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_STRING_TOO_LONG:
+ reason = "RPC_NT_STRING_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PROTSEQ_NOT_FOUND:
+ reason = "RPC_NT_PROTSEQ_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PROCNUM_OUT_OF_RANGE:
+ reason = "RPC_NT_PROCNUM_OUT_OF_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_BINDING_HAS_NO_AUTH:
+ reason = "RPC_NT_BINDING_HAS_NO_AUTH";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_SERVICE:
+ reason = "RPC_NT_UNKNOWN_AUTHN_SERVICE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_LEVEL:
+ reason = "RPC_NT_UNKNOWN_AUTHN_LEVEL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_AUTH_IDENTITY:
+ reason = "RPC_NT_INVALID_AUTH_IDENTITY";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHZ_SERVICE:
+ reason = "RPC_NT_UNKNOWN_AUTHZ_SERVICE";
+ break;
+ case MD_NTSTATUS_WIN_EPT_NT_INVALID_ENTRY:
+ reason = "EPT_NT_INVALID_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_EPT_NT_CANT_PERFORM_OP:
+ reason = "EPT_NT_CANT_PERFORM_OP";
+ break;
+ case MD_NTSTATUS_WIN_EPT_NT_NOT_REGISTERED:
+ reason = "EPT_NT_NOT_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NOTHING_TO_EXPORT:
+ reason = "RPC_NT_NOTHING_TO_EXPORT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INCOMPLETE_NAME:
+ reason = "RPC_NT_INCOMPLETE_NAME";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_VERS_OPTION:
+ reason = "RPC_NT_INVALID_VERS_OPTION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_MORE_MEMBERS:
+ reason = "RPC_NT_NO_MORE_MEMBERS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NOT_ALL_OBJS_UNEXPORTED:
+ reason = "RPC_NT_NOT_ALL_OBJS_UNEXPORTED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INTERFACE_NOT_FOUND:
+ reason = "RPC_NT_INTERFACE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ENTRY_ALREADY_EXISTS:
+ reason = "RPC_NT_ENTRY_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ENTRY_NOT_FOUND:
+ reason = "RPC_NT_ENTRY_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NAME_SERVICE_UNAVAILABLE:
+ reason = "RPC_NT_NAME_SERVICE_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_NAF_ID:
+ reason = "RPC_NT_INVALID_NAF_ID";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CANNOT_SUPPORT:
+ reason = "RPC_NT_CANNOT_SUPPORT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_CONTEXT_AVAILABLE:
+ reason = "RPC_NT_NO_CONTEXT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INTERNAL_ERROR:
+ reason = "RPC_NT_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ZERO_DIVIDE:
+ reason = "RPC_NT_ZERO_DIVIDE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ADDRESS_ERROR:
+ reason = "RPC_NT_ADDRESS_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_FP_DIV_ZERO:
+ reason = "RPC_NT_FP_DIV_ZERO";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_FP_UNDERFLOW:
+ reason = "RPC_NT_FP_UNDERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_FP_OVERFLOW:
+ reason = "RPC_NT_FP_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CALL_IN_PROGRESS:
+ reason = "RPC_NT_CALL_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_MORE_BINDINGS:
+ reason = "RPC_NT_NO_MORE_BINDINGS";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_GROUP_MEMBER_NOT_FOUND:
+ reason = "RPC_NT_GROUP_MEMBER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_EPT_NT_CANT_CREATE:
+ reason = "EPT_NT_CANT_CREATE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_OBJECT:
+ reason = "RPC_NT_INVALID_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_INTERFACES:
+ reason = "RPC_NT_NO_INTERFACES";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_CALL_CANCELLED:
+ reason = "RPC_NT_CALL_CANCELLED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_BINDING_INCOMPLETE:
+ reason = "RPC_NT_BINDING_INCOMPLETE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_COMM_FAILURE:
+ reason = "RPC_NT_COMM_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_AUTHN_LEVEL:
+ reason = "RPC_NT_UNSUPPORTED_AUTHN_LEVEL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_PRINC_NAME:
+ reason = "RPC_NT_NO_PRINC_NAME";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NOT_RPC_ERROR:
+ reason = "RPC_NT_NOT_RPC_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SEC_PKG_ERROR:
+ reason = "RPC_NT_SEC_PKG_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NOT_CANCELLED:
+ reason = "RPC_NT_NOT_CANCELLED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_ASYNC_HANDLE:
+ reason = "RPC_NT_INVALID_ASYNC_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_ASYNC_CALL:
+ reason = "RPC_NT_INVALID_ASYNC_CALL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PROXY_ACCESS_DENIED:
+ reason = "RPC_NT_PROXY_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_COOKIE_AUTH_FAILED:
+ reason = "RPC_NT_COOKIE_AUTH_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NO_MORE_ENTRIES:
+ reason = "RPC_NT_NO_MORE_ENTRIES";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_CHAR_TRANS_OPEN_FAIL:
+ reason = "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_CHAR_TRANS_SHORT_FILE:
+ reason = "RPC_NT_SS_CHAR_TRANS_SHORT_FILE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_IN_NULL_CONTEXT:
+ reason = "RPC_NT_SS_IN_NULL_CONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_CONTEXT_MISMATCH:
+ reason = "RPC_NT_SS_CONTEXT_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_CONTEXT_DAMAGED:
+ reason = "RPC_NT_SS_CONTEXT_DAMAGED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_HANDLES_MISMATCH:
+ reason = "RPC_NT_SS_HANDLES_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_SS_CANNOT_GET_CALL_HANDLE:
+ reason = "RPC_NT_SS_CANNOT_GET_CALL_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_NULL_REF_POINTER:
+ reason = "RPC_NT_NULL_REF_POINTER";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_ENUM_VALUE_OUT_OF_RANGE:
+ reason = "RPC_NT_ENUM_VALUE_OUT_OF_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_BYTE_COUNT_TOO_SMALL:
+ reason = "RPC_NT_BYTE_COUNT_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_BAD_STUB_DATA:
+ reason = "RPC_NT_BAD_STUB_DATA";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_ES_ACTION:
+ reason = "RPC_NT_INVALID_ES_ACTION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_WRONG_ES_VERSION:
+ reason = "RPC_NT_WRONG_ES_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_WRONG_STUB_VERSION:
+ reason = "RPC_NT_WRONG_STUB_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_PIPE_OBJECT:
+ reason = "RPC_NT_INVALID_PIPE_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_INVALID_PIPE_OPERATION:
+ reason = "RPC_NT_INVALID_PIPE_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_WRONG_PIPE_VERSION:
+ reason = "RPC_NT_WRONG_PIPE_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PIPE_CLOSED:
+ reason = "RPC_NT_PIPE_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PIPE_DISCIPLINE_ERROR:
+ reason = "RPC_NT_PIPE_DISCIPLINE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_RPC_NT_PIPE_EMPTY:
+ reason = "RPC_NT_PIPE_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_BAD_MPS_TABLE:
+ reason = "STATUS_PNP_BAD_MPS_TABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_TRANSLATION_FAILED:
+ reason = "STATUS_PNP_TRANSLATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_IRQ_TRANSLATION_FAILED:
+ reason = "STATUS_PNP_IRQ_TRANSLATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PNP_INVALID_ID:
+ reason = "STATUS_PNP_INVALID_ID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_REISSUE_AS_CACHED:
+ reason = "STATUS_IO_REISSUE_AS_CACHED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NAME_INVALID:
+ reason = "STATUS_CTX_WINSTATION_NAME_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_INVALID_PD:
+ reason = "STATUS_CTX_INVALID_PD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_PD_NOT_FOUND:
+ reason = "STATUS_CTX_PD_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CLOSE_PENDING:
+ reason = "STATUS_CTX_CLOSE_PENDING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_NO_OUTBUF:
+ reason = "STATUS_CTX_NO_OUTBUF";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_INF_NOT_FOUND:
+ reason = "STATUS_CTX_MODEM_INF_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_INVALID_MODEMNAME:
+ reason = "STATUS_CTX_INVALID_MODEMNAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_RESPONSE_ERROR:
+ reason = "STATUS_CTX_RESPONSE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_TIMEOUT:
+ reason = "STATUS_CTX_MODEM_RESPONSE_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_NO_CARRIER:
+ reason = "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE:
+ reason = "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_BUSY:
+ reason = "STATUS_CTX_MODEM_RESPONSE_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_VOICE:
+ reason = "STATUS_CTX_MODEM_RESPONSE_VOICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_TD_ERROR:
+ reason = "STATUS_CTX_TD_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_CLIENT_INVALID:
+ reason = "STATUS_CTX_LICENSE_CLIENT_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_NOT_AVAILABLE:
+ reason = "STATUS_CTX_LICENSE_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_EXPIRED:
+ reason = "STATUS_CTX_LICENSE_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NOT_FOUND:
+ reason = "STATUS_CTX_WINSTATION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NAME_COLLISION:
+ reason = "STATUS_CTX_WINSTATION_NAME_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_BUSY:
+ reason = "STATUS_CTX_WINSTATION_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_BAD_VIDEO_MODE:
+ reason = "STATUS_CTX_BAD_VIDEO_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_GRAPHICS_INVALID:
+ reason = "STATUS_CTX_GRAPHICS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_NOT_CONSOLE:
+ reason = "STATUS_CTX_NOT_CONSOLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_QUERY_TIMEOUT:
+ reason = "STATUS_CTX_CLIENT_QUERY_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CONSOLE_DISCONNECT:
+ reason = "STATUS_CTX_CONSOLE_DISCONNECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CONSOLE_CONNECT:
+ reason = "STATUS_CTX_CONSOLE_CONNECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_DENIED:
+ reason = "STATUS_CTX_SHADOW_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_ACCESS_DENIED:
+ reason = "STATUS_CTX_WINSTATION_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_INVALID_WD:
+ reason = "STATUS_CTX_INVALID_WD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_WD_NOT_FOUND:
+ reason = "STATUS_CTX_WD_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_INVALID:
+ reason = "STATUS_CTX_SHADOW_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_DISABLED:
+ reason = "STATUS_CTX_SHADOW_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RDP_PROTOCOL_ERROR:
+ reason = "STATUS_RDP_PROTOCOL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_LICENSE_NOT_SET:
+ reason = "STATUS_CTX_CLIENT_LICENSE_NOT_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_LICENSE_IN_USE:
+ reason = "STATUS_CTX_CLIENT_LICENSE_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE:
+ reason = "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_NOT_RUNNING:
+ reason = "STATUS_CTX_SHADOW_NOT_RUNNING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_LOGON_DISABLED:
+ reason = "STATUS_CTX_LOGON_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTX_SECURITY_LAYER_ERROR:
+ reason = "STATUS_CTX_SECURITY_LAYER_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TS_INCOMPATIBLE_SESSIONS:
+ reason = "STATUS_TS_INCOMPATIBLE_SESSIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TS_VIDEO_SUBSYSTEM_ERROR:
+ reason = "STATUS_TS_VIDEO_SUBSYSTEM_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_FILE_NOT_FOUND:
+ reason = "STATUS_MUI_FILE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_INVALID_FILE:
+ reason = "STATUS_MUI_INVALID_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_INVALID_RC_CONFIG:
+ reason = "STATUS_MUI_INVALID_RC_CONFIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_INVALID_LOCALE_NAME:
+ reason = "STATUS_MUI_INVALID_LOCALE_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME:
+ reason = "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MUI_FILE_NOT_LOADED:
+ reason = "STATUS_MUI_FILE_NOT_LOADED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCE_ENUM_USER_STOP:
+ reason = "STATUS_RESOURCE_ENUM_USER_STOP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NODE:
+ reason = "STATUS_CLUSTER_INVALID_NODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_EXISTS:
+ reason = "STATUS_CLUSTER_NODE_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_JOIN_IN_PROGRESS:
+ reason = "STATUS_CLUSTER_JOIN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_FOUND:
+ reason = "STATUS_CLUSTER_NODE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND:
+ reason = "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_EXISTS:
+ reason = "STATUS_CLUSTER_NETWORK_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_NOT_FOUND:
+ reason = "STATUS_CLUSTER_NETWORK_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NETINTERFACE_EXISTS:
+ reason = "STATUS_CLUSTER_NETINTERFACE_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NETINTERFACE_NOT_FOUND:
+ reason = "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_REQUEST:
+ reason = "STATUS_CLUSTER_INVALID_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NETWORK_PROVIDER:
+ reason = "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_DOWN:
+ reason = "STATUS_CLUSTER_NODE_DOWN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_UNREACHABLE:
+ reason = "STATUS_CLUSTER_NODE_UNREACHABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_MEMBER:
+ reason = "STATUS_CLUSTER_NODE_NOT_MEMBER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS:
+ reason = "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NETWORK:
+ reason = "STATUS_CLUSTER_INVALID_NETWORK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NO_NET_ADAPTERS:
+ reason = "STATUS_CLUSTER_NO_NET_ADAPTERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_UP:
+ reason = "STATUS_CLUSTER_NODE_UP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_PAUSED:
+ reason = "STATUS_CLUSTER_NODE_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_PAUSED:
+ reason = "STATUS_CLUSTER_NODE_NOT_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NO_SECURITY_CONTEXT:
+ reason = "STATUS_CLUSTER_NO_SECURITY_CONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_NOT_INTERNAL:
+ reason = "STATUS_CLUSTER_NETWORK_NOT_INTERNAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_POISONED:
+ reason = "STATUS_CLUSTER_POISONED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_NON_CSV_PATH:
+ reason = "STATUS_CLUSTER_NON_CSV_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_NOT_LOCAL:
+ reason = "STATUS_CLUSTER_CSV_VOLUME_NOT_LOCAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_READ_OPLOCK_BREAK_IN_PROGRESS:
+ reason = "STATUS_CLUSTER_CSV_READ_OPLOCK_BREAK_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_AUTO_PAUSE_ERROR:
+ reason = "STATUS_CLUSTER_CSV_AUTO_PAUSE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_REDIRECTED:
+ reason = "STATUS_CLUSTER_CSV_REDIRECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_NOT_REDIRECTED:
+ reason = "STATUS_CLUSTER_CSV_NOT_REDIRECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_DRAINING:
+ reason = "STATUS_CLUSTER_CSV_VOLUME_DRAINING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_SNAPSHOT_CREATION_IN_PROGRESS:
+ reason = "STATUS_CLUSTER_CSV_SNAPSHOT_CREATION_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_DRAINING_SUCCEEDED_DOWNLEVEL:
+ reason = "STATUS_CLUSTER_CSV_VOLUME_DRAINING_SUCCEEDED_DOWNLEVEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_OPCODE:
+ reason = "STATUS_ACPI_INVALID_OPCODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_STACK_OVERFLOW:
+ reason = "STATUS_ACPI_STACK_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_ASSERT_FAILED:
+ reason = "STATUS_ACPI_ASSERT_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_INDEX:
+ reason = "STATUS_ACPI_INVALID_INDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ARGUMENT:
+ reason = "STATUS_ACPI_INVALID_ARGUMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_FATAL:
+ reason = "STATUS_ACPI_FATAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_SUPERNAME:
+ reason = "STATUS_ACPI_INVALID_SUPERNAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ARGTYPE:
+ reason = "STATUS_ACPI_INVALID_ARGTYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_OBJTYPE:
+ reason = "STATUS_ACPI_INVALID_OBJTYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_TARGETTYPE:
+ reason = "STATUS_ACPI_INVALID_TARGETTYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INCORRECT_ARGUMENT_COUNT:
+ reason = "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_ADDRESS_NOT_MAPPED:
+ reason = "STATUS_ACPI_ADDRESS_NOT_MAPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_EVENTTYPE:
+ reason = "STATUS_ACPI_INVALID_EVENTTYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_HANDLER_COLLISION:
+ reason = "STATUS_ACPI_HANDLER_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_DATA:
+ reason = "STATUS_ACPI_INVALID_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_REGION:
+ reason = "STATUS_ACPI_INVALID_REGION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ACCESS_SIZE:
+ reason = "STATUS_ACPI_INVALID_ACCESS_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_ACQUIRE_GLOBAL_LOCK:
+ reason = "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_ALREADY_INITIALIZED:
+ reason = "STATUS_ACPI_ALREADY_INITIALIZED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_NOT_INITIALIZED:
+ reason = "STATUS_ACPI_NOT_INITIALIZED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_MUTEX_LEVEL:
+ reason = "STATUS_ACPI_INVALID_MUTEX_LEVEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_MUTEX_NOT_OWNED:
+ reason = "STATUS_ACPI_MUTEX_NOT_OWNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_MUTEX_NOT_OWNER:
+ reason = "STATUS_ACPI_MUTEX_NOT_OWNER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_RS_ACCESS:
+ reason = "STATUS_ACPI_RS_ACCESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_TABLE:
+ reason = "STATUS_ACPI_INVALID_TABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_REG_HANDLER_FAILED:
+ reason = "STATUS_ACPI_REG_HANDLER_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ACPI_POWER_REQUEST_FAILED:
+ reason = "STATUS_ACPI_POWER_REQUEST_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_SECTION_NOT_FOUND:
+ reason = "STATUS_SXS_SECTION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_CANT_GEN_ACTCTX:
+ reason = "STATUS_SXS_CANT_GEN_ACTCTX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_INVALID_ACTCTXDATA_FORMAT:
+ reason = "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_NOT_FOUND:
+ reason = "STATUS_SXS_ASSEMBLY_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_FORMAT_ERROR:
+ reason = "STATUS_SXS_MANIFEST_FORMAT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_PARSE_ERROR:
+ reason = "STATUS_SXS_MANIFEST_PARSE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_ACTIVATION_CONTEXT_DISABLED:
+ reason = "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_KEY_NOT_FOUND:
+ reason = "STATUS_SXS_KEY_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_VERSION_CONFLICT:
+ reason = "STATUS_SXS_VERSION_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_WRONG_SECTION_TYPE:
+ reason = "STATUS_SXS_WRONG_SECTION_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_THREAD_QUERIES_DISABLED:
+ reason = "STATUS_SXS_THREAD_QUERIES_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_MISSING:
+ reason = "STATUS_SXS_ASSEMBLY_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET:
+ reason = "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_EARLY_DEACTIVATION:
+ reason = "STATUS_SXS_EARLY_DEACTIVATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_INVALID_DEACTIVATION:
+ reason = "STATUS_SXS_INVALID_DEACTIVATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_MULTIPLE_DEACTIVATION:
+ reason = "STATUS_SXS_MULTIPLE_DEACTIVATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY:
+ reason = "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_PROCESS_TERMINATION_REQUESTED:
+ reason = "STATUS_SXS_PROCESS_TERMINATION_REQUESTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_CORRUPT_ACTIVATION_STACK:
+ reason = "STATUS_SXS_CORRUPT_ACTIVATION_STACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_CORRUPTION:
+ reason = "STATUS_SXS_CORRUPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE:
+ reason = "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME:
+ reason = "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE:
+ reason = "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_IDENTITY_PARSE_ERROR:
+ reason = "STATUS_SXS_IDENTITY_PARSE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_COMPONENT_STORE_CORRUPT:
+ reason = "STATUS_SXS_COMPONENT_STORE_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_FILE_HASH_MISMATCH:
+ reason = "STATUS_SXS_FILE_HASH_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT:
+ reason = "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_IDENTITIES_DIFFERENT:
+ reason = "STATUS_SXS_IDENTITIES_DIFFERENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT:
+ reason = "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY:
+ reason = "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ADVANCED_INSTALLER_FAILED:
+ reason = "STATUS_ADVANCED_INSTALLER_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_XML_ENCODING_MISMATCH:
+ reason = "STATUS_XML_ENCODING_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_TOO_BIG:
+ reason = "STATUS_SXS_MANIFEST_TOO_BIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_SETTING_NOT_REGISTERED:
+ reason = "STATUS_SXS_SETTING_NOT_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE:
+ reason = "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SMI_PRIMITIVE_INSTALLER_FAILED:
+ reason = "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GENERIC_COMMAND_FAILED:
+ reason = "STATUS_GENERIC_COMMAND_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SXS_FILE_HASH_MISSING:
+ reason = "STATUS_SXS_FILE_HASH_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONAL_CONFLICT:
+ reason = "STATUS_TRANSACTIONAL_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INVALID_TRANSACTION:
+ reason = "STATUS_INVALID_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ACTIVE:
+ reason = "STATUS_TRANSACTION_NOT_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TM_INITIALIZATION_FAILED:
+ reason = "STATUS_TM_INITIALIZATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RM_NOT_ACTIVE:
+ reason = "STATUS_RM_NOT_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RM_METADATA_CORRUPT:
+ reason = "STATUS_RM_METADATA_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_JOINED:
+ reason = "STATUS_TRANSACTION_NOT_JOINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_RM:
+ reason = "STATUS_DIRECTORY_NOT_RM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE:
+ reason = "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_RESIZE_INVALID_SIZE:
+ reason = "STATUS_LOG_RESIZE_INVALID_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_REMOTE_FILE_VERSION_MISMATCH:
+ reason = "STATUS_REMOTE_FILE_VERSION_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CRM_PROTOCOL_ALREADY_EXISTS:
+ reason = "STATUS_CRM_PROTOCOL_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_PROPAGATION_FAILED:
+ reason = "STATUS_TRANSACTION_PROPAGATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CRM_PROTOCOL_NOT_FOUND:
+ reason = "STATUS_CRM_PROTOCOL_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_SUPERIOR_EXISTS:
+ reason = "STATUS_TRANSACTION_SUPERIOR_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_REQUEST_NOT_VALID:
+ reason = "STATUS_TRANSACTION_REQUEST_NOT_VALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_REQUESTED:
+ reason = "STATUS_TRANSACTION_NOT_REQUESTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_ALREADY_ABORTED:
+ reason = "STATUS_TRANSACTION_ALREADY_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_ALREADY_COMMITTED:
+ reason = "STATUS_TRANSACTION_ALREADY_COMMITTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER:
+ reason = "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CURRENT_TRANSACTION_NOT_VALID:
+ reason = "STATUS_CURRENT_TRANSACTION_NOT_VALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_GROWTH_FAILED:
+ reason = "STATUS_LOG_GROWTH_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OBJECT_NO_LONGER_EXISTS:
+ reason = "STATUS_OBJECT_NO_LONGER_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STREAM_MINIVERSION_NOT_FOUND:
+ reason = "STATUS_STREAM_MINIVERSION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_STREAM_MINIVERSION_NOT_VALID:
+ reason = "STATUS_STREAM_MINIVERSION_NOT_VALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION:
+ reason = "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT:
+ reason = "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS:
+ reason = "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HANDLE_NO_LONGER_VALID:
+ reason = "STATUS_HANDLE_NO_LONGER_VALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CORRUPTION_DETECTED:
+ reason = "STATUS_LOG_CORRUPTION_DETECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RM_DISCONNECTED:
+ reason = "STATUS_RM_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENLISTMENT_NOT_SUPERIOR:
+ reason = "STATUS_ENLISTMENT_NOT_SUPERIOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FILE_IDENTITY_NOT_PERSISTENT:
+ reason = "STATUS_FILE_IDENTITY_NOT_PERSISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY:
+ reason = "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANT_CROSS_RM_BOUNDARY:
+ reason = "STATUS_CANT_CROSS_RM_BOUNDARY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TXF_DIR_NOT_EMPTY:
+ reason = "STATUS_TXF_DIR_NOT_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_INDOUBT_TRANSACTIONS_EXIST:
+ reason = "STATUS_INDOUBT_TRANSACTIONS_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TM_VOLATILE:
+ reason = "STATUS_TM_VOLATILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ROLLBACK_TIMER_EXPIRED:
+ reason = "STATUS_ROLLBACK_TIMER_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TXF_ATTRIBUTE_CORRUPT:
+ reason = "STATUS_TXF_ATTRIBUTE_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION:
+ reason = "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED:
+ reason = "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE:
+ reason = "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_REQUIRED_PROMOTION:
+ reason = "STATUS_TRANSACTION_REQUIRED_PROMOTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION:
+ reason = "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONS_NOT_FROZEN:
+ reason = "STATUS_TRANSACTIONS_NOT_FROZEN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_FREEZE_IN_PROGRESS:
+ reason = "STATUS_TRANSACTION_FREEZE_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NOT_SNAPSHOT_VOLUME:
+ reason = "STATUS_NOT_SNAPSHOT_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_SAVEPOINT_WITH_OPEN_FILES:
+ reason = "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION:
+ reason = "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TM_IDENTITY_MISMATCH:
+ reason = "STATUS_TM_IDENTITY_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLOATED_SECTION:
+ reason = "STATUS_FLOATED_SECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_ACCEPT_TRANSACTED_WORK:
+ reason = "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CANNOT_ABORT_TRANSACTIONS:
+ reason = "STATUS_CANNOT_ABORT_TRANSACTIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_FOUND:
+ reason = "STATUS_TRANSACTION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RESOURCEMANAGER_NOT_FOUND:
+ reason = "STATUS_RESOURCEMANAGER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_ENLISTMENT_NOT_FOUND:
+ reason = "STATUS_ENLISTMENT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_NOT_FOUND:
+ reason = "STATUS_TRANSACTIONMANAGER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_NOT_ONLINE:
+ reason = "STATUS_TRANSACTIONMANAGER_NOT_ONLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION:
+ reason = "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ROOT:
+ reason = "STATUS_TRANSACTION_NOT_ROOT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_OBJECT_EXPIRED:
+ reason = "STATUS_TRANSACTION_OBJECT_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION:
+ reason = "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED:
+ reason = "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_RECORD_TOO_LONG:
+ reason = "STATUS_TRANSACTION_RECORD_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NO_LINK_TRACKING_IN_TRANSACTION:
+ reason = "STATUS_NO_LINK_TRACKING_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION:
+ reason = "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_INTEGRITY_VIOLATED:
+ reason = "STATUS_TRANSACTION_INTEGRITY_VIOLATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_IDENTITY_MISMATCH:
+ reason = "STATUS_TRANSACTIONMANAGER_IDENTITY_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RM_CANNOT_BE_FROZEN_FOR_SNAPSHOT:
+ reason = "STATUS_RM_CANNOT_BE_FROZEN_FOR_SNAPSHOT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_MUST_WRITETHROUGH:
+ reason = "STATUS_TRANSACTION_MUST_WRITETHROUGH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_SUPERIOR:
+ reason = "STATUS_TRANSACTION_NO_SUPERIOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_EXPIRED_HANDLE:
+ reason = "STATUS_EXPIRED_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ENLISTED:
+ reason = "STATUS_TRANSACTION_NOT_ENLISTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_INVALID:
+ reason = "STATUS_LOG_SECTOR_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_PARITY_INVALID:
+ reason = "STATUS_LOG_SECTOR_PARITY_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_REMAPPED:
+ reason = "STATUS_LOG_SECTOR_REMAPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_INCOMPLETE:
+ reason = "STATUS_LOG_BLOCK_INCOMPLETE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_INVALID_RANGE:
+ reason = "STATUS_LOG_INVALID_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_BLOCKS_EXHAUSTED:
+ reason = "STATUS_LOG_BLOCKS_EXHAUSTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_READ_CONTEXT_INVALID:
+ reason = "STATUS_LOG_READ_CONTEXT_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_RESTART_INVALID:
+ reason = "STATUS_LOG_RESTART_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_VERSION:
+ reason = "STATUS_LOG_BLOCK_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_INVALID:
+ reason = "STATUS_LOG_BLOCK_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_READ_MODE_INVALID:
+ reason = "STATUS_LOG_READ_MODE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_METADATA_CORRUPT:
+ reason = "STATUS_LOG_METADATA_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_METADATA_INVALID:
+ reason = "STATUS_LOG_METADATA_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_METADATA_INCONSISTENT:
+ reason = "STATUS_LOG_METADATA_INCONSISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_RESERVATION_INVALID:
+ reason = "STATUS_LOG_RESERVATION_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CANT_DELETE:
+ reason = "STATUS_LOG_CANT_DELETE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_LIMIT_EXCEEDED:
+ reason = "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_START_OF_LOG:
+ reason = "STATUS_LOG_START_OF_LOG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_POLICY_ALREADY_INSTALLED:
+ reason = "STATUS_LOG_POLICY_ALREADY_INSTALLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_POLICY_NOT_INSTALLED:
+ reason = "STATUS_LOG_POLICY_NOT_INSTALLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_POLICY_INVALID:
+ reason = "STATUS_LOG_POLICY_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_POLICY_CONFLICT:
+ reason = "STATUS_LOG_POLICY_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_PINNED_ARCHIVE_TAIL:
+ reason = "STATUS_LOG_PINNED_ARCHIVE_TAIL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_RECORD_NONEXISTENT:
+ reason = "STATUS_LOG_RECORD_NONEXISTENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_RECORDS_RESERVED_INVALID:
+ reason = "STATUS_LOG_RECORDS_RESERVED_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_SPACE_RESERVED_INVALID:
+ reason = "STATUS_LOG_SPACE_RESERVED_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_TAIL_INVALID:
+ reason = "STATUS_LOG_TAIL_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_FULL:
+ reason = "STATUS_LOG_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_MULTIPLEXED:
+ reason = "STATUS_LOG_MULTIPLEXED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_DEDICATED:
+ reason = "STATUS_LOG_DEDICATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS:
+ reason = "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_ARCHIVE_IN_PROGRESS:
+ reason = "STATUS_LOG_ARCHIVE_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_EPHEMERAL:
+ reason = "STATUS_LOG_EPHEMERAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_NOT_ENOUGH_CONTAINERS:
+ reason = "STATUS_LOG_NOT_ENOUGH_CONTAINERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CLIENT_ALREADY_REGISTERED:
+ reason = "STATUS_LOG_CLIENT_ALREADY_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CLIENT_NOT_REGISTERED:
+ reason = "STATUS_LOG_CLIENT_NOT_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_FULL_HANDLER_IN_PROGRESS:
+ reason = "STATUS_LOG_FULL_HANDLER_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_READ_FAILED:
+ reason = "STATUS_LOG_CONTAINER_READ_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_WRITE_FAILED:
+ reason = "STATUS_LOG_CONTAINER_WRITE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_OPEN_FAILED:
+ reason = "STATUS_LOG_CONTAINER_OPEN_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_STATE_INVALID:
+ reason = "STATUS_LOG_CONTAINER_STATE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_STATE_INVALID:
+ reason = "STATUS_LOG_STATE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_PINNED:
+ reason = "STATUS_LOG_PINNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_METADATA_FLUSH_FAILED:
+ reason = "STATUS_LOG_METADATA_FLUSH_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_INCONSISTENT_SECURITY:
+ reason = "STATUS_LOG_INCONSISTENT_SECURITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_APPENDED_FLUSH_FAILED:
+ reason = "STATUS_LOG_APPENDED_FLUSH_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_LOG_PINNED_RESERVATION:
+ reason = "STATUS_LOG_PINNED_RESERVATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD:
+ reason = "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NO_HANDLER_DEFINED:
+ reason = "STATUS_FLT_NO_HANDLER_DEFINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALREADY_DEFINED:
+ reason = "STATUS_FLT_CONTEXT_ALREADY_DEFINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST:
+ reason = "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_DISALLOW_FAST_IO:
+ reason = "STATUS_FLT_DISALLOW_FAST_IO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INVALID_NAME_REQUEST:
+ reason = "STATUS_FLT_INVALID_NAME_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NOT_SAFE_TO_POST_OPERATION:
+ reason = "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NOT_INITIALIZED:
+ reason = "STATUS_FLT_NOT_INITIALIZED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_FILTER_NOT_READY:
+ reason = "STATUS_FLT_FILTER_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_POST_OPERATION_CLEANUP:
+ reason = "STATUS_FLT_POST_OPERATION_CLEANUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INTERNAL_ERROR:
+ reason = "STATUS_FLT_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_DELETING_OBJECT:
+ reason = "STATUS_FLT_DELETING_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_MUST_BE_NONPAGED_POOL:
+ reason = "STATUS_FLT_MUST_BE_NONPAGED_POOL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_DUPLICATE_ENTRY:
+ reason = "STATUS_FLT_DUPLICATE_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_CBDQ_DISABLED:
+ reason = "STATUS_FLT_CBDQ_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_DO_NOT_ATTACH:
+ reason = "STATUS_FLT_DO_NOT_ATTACH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_DO_NOT_DETACH:
+ reason = "STATUS_FLT_DO_NOT_DETACH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_ALTITUDE_COLLISION:
+ reason = "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_NAME_COLLISION:
+ reason = "STATUS_FLT_INSTANCE_NAME_COLLISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_FILTER_NOT_FOUND:
+ reason = "STATUS_FLT_FILTER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_VOLUME_NOT_FOUND:
+ reason = "STATUS_FLT_VOLUME_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_NOT_FOUND:
+ reason = "STATUS_FLT_INSTANCE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND:
+ reason = "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_INVALID_CONTEXT_REGISTRATION:
+ reason = "STATUS_FLT_INVALID_CONTEXT_REGISTRATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NAME_CACHE_MISS:
+ reason = "STATUS_FLT_NAME_CACHE_MISS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NO_DEVICE_OBJECT:
+ reason = "STATUS_FLT_NO_DEVICE_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_VOLUME_ALREADY_MOUNTED:
+ reason = "STATUS_FLT_VOLUME_ALREADY_MOUNTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_ALREADY_ENLISTED:
+ reason = "STATUS_FLT_ALREADY_ENLISTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALREADY_LINKED:
+ reason = "STATUS_FLT_CONTEXT_ALREADY_LINKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_NO_WAITER_FOR_REPLY:
+ reason = "STATUS_FLT_NO_WAITER_FOR_REPLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FLT_REGISTRATION_BUSY:
+ reason = "STATUS_FLT_REGISTRATION_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_NO_DESCRIPTOR:
+ reason = "STATUS_MONITOR_NO_DESCRIPTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT:
+ reason = "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM:
+ reason = "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK:
+ reason = "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED:
+ reason = "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK:
+ reason = "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK:
+ reason = "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA:
+ reason = "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK:
+ reason = "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_MANUFACTURE_DATE:
+ reason = "STATUS_MONITOR_INVALID_MANUFACTURE_DATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER:
+ reason = "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER:
+ reason = "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER:
+ reason = "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_WAS_RESET:
+ reason = "STATUS_GRAPHICS_ADAPTER_WAS_RESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_DRIVER_MODEL:
+ reason = "STATUS_GRAPHICS_INVALID_DRIVER_MODEL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_MODE_CHANGED:
+ reason = "STATUS_GRAPHICS_PRESENT_MODE_CHANGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_OCCLUDED:
+ reason = "STATUS_GRAPHICS_PRESENT_OCCLUDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_DENIED:
+ reason = "STATUS_GRAPHICS_PRESENT_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANNOTCOLORCONVERT:
+ reason = "STATUS_GRAPHICS_CANNOTCOLORCONVERT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DRIVER_MISMATCH:
+ reason = "STATUS_GRAPHICS_DRIVER_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_REDIRECTION_DISABLED:
+ reason = "STATUS_GRAPHICS_PRESENT_REDIRECTION_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_UNOCCLUDED:
+ reason = "STATUS_GRAPHICS_PRESENT_UNOCCLUDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_WINDOWDC_NOT_AVAILABLE:
+ reason = "STATUS_GRAPHICS_WINDOWDC_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_WINDOWLESS_PRESENT_DISABLED:
+ reason = "STATUS_GRAPHICS_WINDOWLESS_PRESENT_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_VIDEO_MEMORY:
+ reason = "STATUS_GRAPHICS_NO_VIDEO_MEMORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_LOCK_MEMORY:
+ reason = "STATUS_GRAPHICS_CANT_LOCK_MEMORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_BUSY:
+ reason = "STATUS_GRAPHICS_ALLOCATION_BUSY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TOO_MANY_REFERENCES:
+ reason = "STATUS_GRAPHICS_TOO_MANY_REFERENCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TRY_AGAIN_LATER:
+ reason = "STATUS_GRAPHICS_TRY_AGAIN_LATER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TRY_AGAIN_NOW:
+ reason = "STATUS_GRAPHICS_TRY_AGAIN_NOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_INVALID:
+ reason = "STATUS_GRAPHICS_ALLOCATION_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE:
+ reason = "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED:
+ reason = "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION:
+ reason = "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE:
+ reason = "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION:
+ reason = "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_CLOSED:
+ reason = "STATUS_GRAPHICS_ALLOCATION_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE:
+ reason = "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE:
+ reason = "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE:
+ reason = "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST:
+ reason = "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE:
+ reason = "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_FREQUENCY:
+ reason = "STATUS_GRAPHICS_INVALID_FREQUENCY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ACTIVE_REGION:
+ reason = "STATUS_GRAPHICS_INVALID_ACTIVE_REGION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_TOTAL_REGION:
+ reason = "STATUS_GRAPHICS_INVALID_TOTAL_REGION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET:
+ reason = "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET:
+ reason = "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET:
+ reason = "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET:
+ reason = "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_ALREADY_IN_SET:
+ reason = "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET:
+ reason = "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET:
+ reason = "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_STALE_MODESET:
+ reason = "STATUS_GRAPHICS_STALE_MODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN:
+ reason = "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE:
+ reason = "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION:
+ reason = "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES:
+ reason = "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE:
+ reason = "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET:
+ reason = "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET:
+ reason = "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR:
+ reason = "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET:
+ reason = "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET:
+ reason = "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE:
+ reason = "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_RESOURCES_NOT_RELATED:
+ reason = "STATUS_GRAPHICS_RESOURCES_NOT_RELATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE:
+ reason = "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE:
+ reason = "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET:
+ reason = "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER:
+ reason = "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_VIDPNMGR:
+ reason = "STATUS_GRAPHICS_NO_VIDPNMGR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_ACTIVE_VIDPN:
+ reason = "STATUS_GRAPHICS_NO_ACTIVE_VIDPN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_NOT_CONNECTED:
+ reason = "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE:
+ reason = "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE:
+ reason = "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_STRIDE:
+ reason = "STATUS_GRAPHICS_INVALID_STRIDE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PIXELFORMAT:
+ reason = "STATUS_GRAPHICS_INVALID_PIXELFORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_COLORBASIS:
+ reason = "STATUS_GRAPHICS_INVALID_COLORBASIS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE:
+ reason = "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY:
+ reason = "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT:
+ reason = "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE:
+ reason = "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN:
+ reason = "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL:
+ reason = "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION:
+ reason = "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_GAMMA_RAMP:
+ reason = "STATUS_GRAPHICS_INVALID_GAMMA_RAMP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_NOT_IN_MODESET:
+ reason = "STATUS_GRAPHICS_MODE_NOT_IN_MODESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON:
+ reason = "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE:
+ reason = "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE:
+ reason = "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS:
+ reason = "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING:
+ reason = "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED:
+ reason = "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS:
+ reason = "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT:
+ reason = "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM:
+ reason = "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT:
+ reason = "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED:
+ reason = "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION:
+ reason = "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_CLIENT_TYPE:
+ reason = "STATUS_GRAPHICS_INVALID_CLIENT_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET:
+ reason = "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED:
+ reason = "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER:
+ reason = "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED:
+ reason = "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED:
+ reason = "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY:
+ reason = "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED:
+ reason = "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON:
+ reason = "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE:
+ reason = "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER:
+ reason = "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED:
+ reason = "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_OPM_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_COPP_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_COPP_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_UAB_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_UAB_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS:
+ reason = "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST:
+ reason = "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INTERNAL_ERROR:
+ reason = "STATUS_GRAPHICS_OPM_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_HANDLE:
+ reason = "STATUS_GRAPHICS_OPM_INVALID_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH:
+ reason = "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED:
+ reason = "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED:
+ reason = "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PVP_HFS_FAILED:
+ reason = "STATUS_GRAPHICS_PVP_HFS_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_SRM:
+ reason = "STATUS_GRAPHICS_OPM_INVALID_SRM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP:
+ reason = "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP:
+ reason = "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA:
+ reason = "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET:
+ reason = "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH:
+ reason = "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE:
+ reason = "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS:
+ reason = "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS:
+ reason = "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST:
+ reason = "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR:
+ reason = "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS:
+ reason = "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST:
+ reason = "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_I2C_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST:
+ reason = "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA:
+ reason = "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA:
+ reason = "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_DATA:
+ reason = "STATUS_GRAPHICS_DDCCI_INVALID_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE:
+ reason = "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING:
+ reason = "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MCA_INTERNAL_ERROR:
+ reason = "STATUS_GRAPHICS_MCA_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND:
+ reason = "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH:
+ reason = "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM:
+ reason = "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE:
+ reason = "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS:
+ reason = "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED:
+ reason = "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME:
+ reason = "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP:
+ reason = "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED:
+ reason = "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_POINTER:
+ reason = "STATUS_GRAPHICS_INVALID_POINTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE:
+ reason = "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL:
+ reason = "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_INTERNAL_ERROR:
+ reason = "STATUS_GRAPHICS_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS:
+ reason = "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_LOCKED_VOLUME:
+ reason = "STATUS_FVE_LOCKED_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_ENCRYPTED:
+ reason = "STATUS_FVE_NOT_ENCRYPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_BAD_INFORMATION:
+ reason = "STATUS_FVE_BAD_INFORMATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_TOO_SMALL:
+ reason = "STATUS_FVE_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FAILED_WRONG_FS:
+ reason = "STATUS_FVE_FAILED_WRONG_FS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_BAD_PARTITION_SIZE:
+ reason = "STATUS_FVE_BAD_PARTITION_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FS_NOT_EXTENDED:
+ reason = "STATUS_FVE_FS_NOT_EXTENDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FS_MOUNTED:
+ reason = "STATUS_FVE_FS_MOUNTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NO_LICENSE:
+ reason = "STATUS_FVE_NO_LICENSE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_ACTION_NOT_ALLOWED:
+ reason = "STATUS_FVE_ACTION_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_BAD_DATA:
+ reason = "STATUS_FVE_BAD_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_NOT_BOUND:
+ reason = "STATUS_FVE_VOLUME_NOT_BOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_DATA_VOLUME:
+ reason = "STATUS_FVE_NOT_DATA_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_CONV_READ_ERROR:
+ reason = "STATUS_FVE_CONV_READ_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_CONV_WRITE_ERROR:
+ reason = "STATUS_FVE_CONV_WRITE_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_OVERLAPPED_UPDATE:
+ reason = "STATUS_FVE_OVERLAPPED_UPDATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FAILED_SECTOR_SIZE:
+ reason = "STATUS_FVE_FAILED_SECTOR_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FAILED_AUTHENTICATION:
+ reason = "STATUS_FVE_FAILED_AUTHENTICATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_OS_VOLUME:
+ reason = "STATUS_FVE_NOT_OS_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_NOT_FOUND:
+ reason = "STATUS_FVE_KEYFILE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_INVALID:
+ reason = "STATUS_FVE_KEYFILE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_NO_VMK:
+ reason = "STATUS_FVE_KEYFILE_NO_VMK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_TPM_DISABLED:
+ reason = "STATUS_FVE_TPM_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO:
+ reason = "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_TPM_INVALID_PCR:
+ reason = "STATUS_FVE_TPM_INVALID_PCR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_TPM_NO_VMK:
+ reason = "STATUS_FVE_TPM_NO_VMK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_PIN_INVALID:
+ reason = "STATUS_FVE_PIN_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_AUTH_INVALID_APPLICATION:
+ reason = "STATUS_FVE_AUTH_INVALID_APPLICATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_AUTH_INVALID_CONFIG:
+ reason = "STATUS_FVE_AUTH_INVALID_CONFIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_DEBUGGER_ENABLED:
+ reason = "STATUS_FVE_DEBUGGER_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_DRY_RUN_FAILED:
+ reason = "STATUS_FVE_DRY_RUN_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_BAD_METADATA_POINTER:
+ reason = "STATUS_FVE_BAD_METADATA_POINTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_OLD_METADATA_COPY:
+ reason = "STATUS_FVE_OLD_METADATA_COPY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_REBOOT_REQUIRED:
+ reason = "STATUS_FVE_REBOOT_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_RAW_ACCESS:
+ reason = "STATUS_FVE_RAW_ACCESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_RAW_BLOCKED:
+ reason = "STATUS_FVE_RAW_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NO_AUTOUNLOCK_MASTER_KEY:
+ reason = "STATUS_FVE_NO_AUTOUNLOCK_MASTER_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_MOR_FAILED:
+ reason = "STATUS_FVE_MOR_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NO_FEATURE_LICENSE:
+ reason = "STATUS_FVE_NO_FEATURE_LICENSE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_POLICY_USER_DISABLE_RDV_NOT_ALLOWED:
+ reason = "STATUS_FVE_POLICY_USER_DISABLE_RDV_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_CONV_RECOVERY_FAILED:
+ reason = "STATUS_FVE_CONV_RECOVERY_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_VIRTUALIZED_SPACE_TOO_BIG:
+ reason = "STATUS_FVE_VIRTUALIZED_SPACE_TOO_BIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_INVALID_DATUM_TYPE:
+ reason = "STATUS_FVE_INVALID_DATUM_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_TOO_SMALL:
+ reason = "STATUS_FVE_VOLUME_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_ENH_PIN_INVALID:
+ reason = "STATUS_FVE_ENH_PIN_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_FULL_ENCRYPTION_NOT_ALLOWED_ON_TP_STORAGE:
+ reason = "STATUS_FVE_FULL_ENCRYPTION_NOT_ALLOWED_ON_TP_STORAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_WIPE_NOT_ALLOWED_ON_TP_STORAGE:
+ reason = "STATUS_FVE_WIPE_NOT_ALLOWED_ON_TP_STORAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_ON_CSV_STACK:
+ reason = "STATUS_FVE_NOT_ALLOWED_ON_CSV_STACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_ON_CLUSTER:
+ reason = "STATUS_FVE_NOT_ALLOWED_ON_CLUSTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_TO_UPGRADE_WHILE_CONVERTING:
+ reason = "STATUS_FVE_NOT_ALLOWED_TO_UPGRADE_WHILE_CONVERTING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_WIPE_CANCEL_NOT_APPLICABLE:
+ reason = "STATUS_FVE_WIPE_CANCEL_NOT_APPLICABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_EDRIVE_DRY_RUN_FAILED:
+ reason = "STATUS_FVE_EDRIVE_DRY_RUN_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_SECUREBOOT_DISABLED:
+ reason = "STATUS_FVE_SECUREBOOT_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_SECUREBOOT_CONFIG_CHANGE:
+ reason = "STATUS_FVE_SECUREBOOT_CONFIG_CHANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_DEVICE_LOCKEDOUT:
+ reason = "STATUS_FVE_DEVICE_LOCKEDOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_EXTEND_PREVENTS_EOW_DECRYPT:
+ reason = "STATUS_FVE_VOLUME_EXTEND_PREVENTS_EOW_DECRYPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_NOT_DE_VOLUME:
+ reason = "STATUS_FVE_NOT_DE_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_PROTECTION_DISABLED:
+ reason = "STATUS_FVE_PROTECTION_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FVE_PROTECTION_CANNOT_BE_DISABLED:
+ reason = "STATUS_FVE_PROTECTION_CANNOT_BE_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CALLOUT_NOT_FOUND:
+ reason = "STATUS_FWP_CALLOUT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CONDITION_NOT_FOUND:
+ reason = "STATUS_FWP_CONDITION_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_FILTER_NOT_FOUND:
+ reason = "STATUS_FWP_FILTER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_LAYER_NOT_FOUND:
+ reason = "STATUS_FWP_LAYER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_NOT_FOUND:
+ reason = "STATUS_FWP_PROVIDER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND:
+ reason = "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_SUBLAYER_NOT_FOUND:
+ reason = "STATUS_FWP_SUBLAYER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NOT_FOUND:
+ reason = "STATUS_FWP_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_ALREADY_EXISTS:
+ reason = "STATUS_FWP_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_IN_USE:
+ reason = "STATUS_FWP_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS:
+ reason = "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_WRONG_SESSION:
+ reason = "STATUS_FWP_WRONG_SESSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NO_TXN_IN_PROGRESS:
+ reason = "STATUS_FWP_NO_TXN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TXN_IN_PROGRESS:
+ reason = "STATUS_FWP_TXN_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TXN_ABORTED:
+ reason = "STATUS_FWP_TXN_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_SESSION_ABORTED:
+ reason = "STATUS_FWP_SESSION_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_TXN:
+ reason = "STATUS_FWP_INCOMPATIBLE_TXN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TIMEOUT:
+ reason = "STATUS_FWP_TIMEOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NET_EVENTS_DISABLED:
+ reason = "STATUS_FWP_NET_EVENTS_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_LAYER:
+ reason = "STATUS_FWP_INCOMPATIBLE_LAYER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_KM_CLIENTS_ONLY:
+ reason = "STATUS_FWP_KM_CLIENTS_ONLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_LIFETIME_MISMATCH:
+ reason = "STATUS_FWP_LIFETIME_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_BUILTIN_OBJECT:
+ reason = "STATUS_FWP_BUILTIN_OBJECT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TOO_MANY_CALLOUTS:
+ reason = "STATUS_FWP_TOO_MANY_CALLOUTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NOTIFICATION_DROPPED:
+ reason = "STATUS_FWP_NOTIFICATION_DROPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TRAFFIC_MISMATCH:
+ reason = "STATUS_FWP_TRAFFIC_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_SA_STATE:
+ reason = "STATUS_FWP_INCOMPATIBLE_SA_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NULL_POINTER:
+ reason = "STATUS_FWP_NULL_POINTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_ENUMERATOR:
+ reason = "STATUS_FWP_INVALID_ENUMERATOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_FLAGS:
+ reason = "STATUS_FWP_INVALID_FLAGS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_NET_MASK:
+ reason = "STATUS_FWP_INVALID_NET_MASK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_RANGE:
+ reason = "STATUS_FWP_INVALID_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_INTERVAL:
+ reason = "STATUS_FWP_INVALID_INTERVAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_ZERO_LENGTH_ARRAY:
+ reason = "STATUS_FWP_ZERO_LENGTH_ARRAY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NULL_DISPLAY_NAME:
+ reason = "STATUS_FWP_NULL_DISPLAY_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_ACTION_TYPE:
+ reason = "STATUS_FWP_INVALID_ACTION_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_WEIGHT:
+ reason = "STATUS_FWP_INVALID_WEIGHT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_MATCH_TYPE_MISMATCH:
+ reason = "STATUS_FWP_MATCH_TYPE_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TYPE_MISMATCH:
+ reason = "STATUS_FWP_TYPE_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_OUT_OF_BOUNDS:
+ reason = "STATUS_FWP_OUT_OF_BOUNDS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_RESERVED:
+ reason = "STATUS_FWP_RESERVED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_CONDITION:
+ reason = "STATUS_FWP_DUPLICATE_CONDITION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_KEYMOD:
+ reason = "STATUS_FWP_DUPLICATE_KEYMOD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER:
+ reason = "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER:
+ reason = "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER:
+ reason = "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT:
+ reason = "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_AUTH_METHOD:
+ reason = "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_DH_GROUP:
+ reason = "STATUS_FWP_INCOMPATIBLE_DH_GROUP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_EM_NOT_SUPPORTED:
+ reason = "STATUS_FWP_EM_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_NEVER_MATCH:
+ reason = "STATUS_FWP_NEVER_MATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_CONTEXT_MISMATCH:
+ reason = "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_PARAMETER:
+ reason = "STATUS_FWP_INVALID_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TOO_MANY_SUBLAYERS:
+ reason = "STATUS_FWP_TOO_MANY_SUBLAYERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CALLOUT_NOTIFICATION_FAILED:
+ reason = "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_AUTH_TRANSFORM:
+ reason = "STATUS_FWP_INVALID_AUTH_TRANSFORM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_CIPHER_TRANSFORM:
+ reason = "STATUS_FWP_INVALID_CIPHER_TRANSFORM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_CIPHER_TRANSFORM:
+ reason = "STATUS_FWP_INCOMPATIBLE_CIPHER_TRANSFORM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_TRANSFORM_COMBINATION:
+ reason = "STATUS_FWP_INVALID_TRANSFORM_COMBINATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_AUTH_METHOD:
+ reason = "STATUS_FWP_DUPLICATE_AUTH_METHOD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_TUNNEL_ENDPOINT:
+ reason = "STATUS_FWP_INVALID_TUNNEL_ENDPOINT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_L2_DRIVER_NOT_READY:
+ reason = "STATUS_FWP_L2_DRIVER_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_KEY_DICTATOR_ALREADY_REGISTERED:
+ reason = "STATUS_FWP_KEY_DICTATOR_ALREADY_REGISTERED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_KEY_DICTATION_INVALID_KEYING_MATERIAL:
+ reason = "STATUS_FWP_KEY_DICTATION_INVALID_KEYING_MATERIAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CONNECTIONS_DISABLED:
+ reason = "STATUS_FWP_CONNECTIONS_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INVALID_DNS_NAME:
+ reason = "STATUS_FWP_INVALID_DNS_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_STILL_ON:
+ reason = "STATUS_FWP_STILL_ON";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_IKEEXT_NOT_RUNNING:
+ reason = "STATUS_FWP_IKEEXT_NOT_RUNNING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_TCPIP_NOT_READY:
+ reason = "STATUS_FWP_TCPIP_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INJECT_HANDLE_CLOSING:
+ reason = "STATUS_FWP_INJECT_HANDLE_CLOSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_INJECT_HANDLE_STALE:
+ reason = "STATUS_FWP_INJECT_HANDLE_STALE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_CANNOT_PEND:
+ reason = "STATUS_FWP_CANNOT_PEND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_FWP_DROP_NOICMP:
+ reason = "STATUS_FWP_DROP_NOICMP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_CLOSING:
+ reason = "STATUS_NDIS_CLOSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_BAD_VERSION:
+ reason = "STATUS_NDIS_BAD_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_BAD_CHARACTERISTICS:
+ reason = "STATUS_NDIS_BAD_CHARACTERISTICS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_NOT_FOUND:
+ reason = "STATUS_NDIS_ADAPTER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_OPEN_FAILED:
+ reason = "STATUS_NDIS_OPEN_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_DEVICE_FAILED:
+ reason = "STATUS_NDIS_DEVICE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_FULL:
+ reason = "STATUS_NDIS_MULTICAST_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_EXISTS:
+ reason = "STATUS_NDIS_MULTICAST_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_NOT_FOUND:
+ reason = "STATUS_NDIS_MULTICAST_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_REQUEST_ABORTED:
+ reason = "STATUS_NDIS_REQUEST_ABORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_RESET_IN_PROGRESS:
+ reason = "STATUS_NDIS_RESET_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PACKET:
+ reason = "STATUS_NDIS_INVALID_PACKET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_DEVICE_REQUEST:
+ reason = "STATUS_NDIS_INVALID_DEVICE_REQUEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_NOT_READY:
+ reason = "STATUS_NDIS_ADAPTER_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_LENGTH:
+ reason = "STATUS_NDIS_INVALID_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_DATA:
+ reason = "STATUS_NDIS_INVALID_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_BUFFER_TOO_SHORT:
+ reason = "STATUS_NDIS_BUFFER_TOO_SHORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_OID:
+ reason = "STATUS_NDIS_INVALID_OID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_REMOVED:
+ reason = "STATUS_NDIS_ADAPTER_REMOVED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_UNSUPPORTED_MEDIA:
+ reason = "STATUS_NDIS_UNSUPPORTED_MEDIA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_GROUP_ADDRESS_IN_USE:
+ reason = "STATUS_NDIS_GROUP_ADDRESS_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_FILE_NOT_FOUND:
+ reason = "STATUS_NDIS_FILE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_ERROR_READING_FILE:
+ reason = "STATUS_NDIS_ERROR_READING_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_ALREADY_MAPPED:
+ reason = "STATUS_NDIS_ALREADY_MAPPED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_RESOURCE_CONFLICT:
+ reason = "STATUS_NDIS_RESOURCE_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_MEDIA_DISCONNECTED:
+ reason = "STATUS_NDIS_MEDIA_DISCONNECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_ADDRESS:
+ reason = "STATUS_NDIS_INVALID_ADDRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_PAUSED:
+ reason = "STATUS_NDIS_PAUSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INTERFACE_NOT_FOUND:
+ reason = "STATUS_NDIS_INTERFACE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_UNSUPPORTED_REVISION:
+ reason = "STATUS_NDIS_UNSUPPORTED_REVISION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PORT:
+ reason = "STATUS_NDIS_INVALID_PORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PORT_STATE:
+ reason = "STATUS_NDIS_INVALID_PORT_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_LOW_POWER_STATE:
+ reason = "STATUS_NDIS_LOW_POWER_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_REINIT_REQUIRED:
+ reason = "STATUS_NDIS_REINIT_REQUIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_NOT_SUPPORTED:
+ reason = "STATUS_NDIS_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_POLICY:
+ reason = "STATUS_NDIS_OFFLOAD_POLICY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_CONNECTION_REJECTED:
+ reason = "STATUS_NDIS_OFFLOAD_CONNECTION_REJECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_PATH_REJECTED:
+ reason = "STATUS_NDIS_OFFLOAD_PATH_REJECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED:
+ reason = "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_MEDIA_IN_USE:
+ reason = "STATUS_NDIS_DOT11_MEDIA_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_POWER_STATE_INVALID:
+ reason = "STATUS_NDIS_DOT11_POWER_STATE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_PM_WOL_PATTERN_LIST_FULL:
+ reason = "STATUS_NDIS_PM_WOL_PATTERN_LIST_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_NDIS_PM_PROTOCOL_OFFLOAD_LIST_FULL:
+ reason = "STATUS_NDIS_PM_PROTOCOL_OFFLOAD_LIST_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_ERROR_MASK:
+ reason = "STATUS_TPM_ERROR_MASK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUTHFAIL:
+ reason = "STATUS_TPM_AUTHFAIL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BADINDEX:
+ reason = "STATUS_TPM_BADINDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_PARAMETER:
+ reason = "STATUS_TPM_BAD_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAILURE:
+ reason = "STATUS_TPM_AUDITFAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_CLEAR_DISABLED:
+ reason = "STATUS_TPM_CLEAR_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DEACTIVATED:
+ reason = "STATUS_TPM_DEACTIVATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DISABLED:
+ reason = "STATUS_TPM_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DISABLED_CMD:
+ reason = "STATUS_TPM_DISABLED_CMD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_FAIL:
+ reason = "STATUS_TPM_FAIL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_ORDINAL:
+ reason = "STATUS_TPM_BAD_ORDINAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INSTALL_DISABLED:
+ reason = "STATUS_TPM_INSTALL_DISABLED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_KEYHANDLE:
+ reason = "STATUS_TPM_INVALID_KEYHANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_KEYNOTFOUND:
+ reason = "STATUS_TPM_KEYNOTFOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INAPPROPRIATE_ENC:
+ reason = "STATUS_TPM_INAPPROPRIATE_ENC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MIGRATEFAIL:
+ reason = "STATUS_TPM_MIGRATEFAIL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_PCR_INFO:
+ reason = "STATUS_TPM_INVALID_PCR_INFO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOSPACE:
+ reason = "STATUS_TPM_NOSPACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOSRK:
+ reason = "STATUS_TPM_NOSRK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOTSEALED_BLOB:
+ reason = "STATUS_TPM_NOTSEALED_BLOB";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_OWNER_SET:
+ reason = "STATUS_TPM_OWNER_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_RESOURCES:
+ reason = "STATUS_TPM_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_SHORTRANDOM:
+ reason = "STATUS_TPM_SHORTRANDOM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_SIZE:
+ reason = "STATUS_TPM_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_WRONGPCRVAL:
+ reason = "STATUS_TPM_WRONGPCRVAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_PARAM_SIZE:
+ reason = "STATUS_TPM_BAD_PARAM_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_SHA_THREAD:
+ reason = "STATUS_TPM_SHA_THREAD";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_SHA_ERROR:
+ reason = "STATUS_TPM_SHA_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_FAILEDSELFTEST:
+ reason = "STATUS_TPM_FAILEDSELFTEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUTH2FAIL:
+ reason = "STATUS_TPM_AUTH2FAIL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BADTAG:
+ reason = "STATUS_TPM_BADTAG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_IOERROR:
+ reason = "STATUS_TPM_IOERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_ENCRYPT_ERROR:
+ reason = "STATUS_TPM_ENCRYPT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DECRYPT_ERROR:
+ reason = "STATUS_TPM_DECRYPT_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_AUTHHANDLE:
+ reason = "STATUS_TPM_INVALID_AUTHHANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NO_ENDORSEMENT:
+ reason = "STATUS_TPM_NO_ENDORSEMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_KEYUSAGE:
+ reason = "STATUS_TPM_INVALID_KEYUSAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_WRONG_ENTITYTYPE:
+ reason = "STATUS_TPM_WRONG_ENTITYTYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_POSTINIT:
+ reason = "STATUS_TPM_INVALID_POSTINIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INAPPROPRIATE_SIG:
+ reason = "STATUS_TPM_INAPPROPRIATE_SIG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_KEY_PROPERTY:
+ reason = "STATUS_TPM_BAD_KEY_PROPERTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_MIGRATION:
+ reason = "STATUS_TPM_BAD_MIGRATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_SCHEME:
+ reason = "STATUS_TPM_BAD_SCHEME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_DATASIZE:
+ reason = "STATUS_TPM_BAD_DATASIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_MODE:
+ reason = "STATUS_TPM_BAD_MODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_PRESENCE:
+ reason = "STATUS_TPM_BAD_PRESENCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_VERSION:
+ reason = "STATUS_TPM_BAD_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NO_WRAP_TRANSPORT:
+ reason = "STATUS_TPM_NO_WRAP_TRANSPORT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAIL_UNSUCCESSFUL:
+ reason = "STATUS_TPM_AUDITFAIL_UNSUCCESSFUL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAIL_SUCCESSFUL:
+ reason = "STATUS_TPM_AUDITFAIL_SUCCESSFUL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOTRESETABLE:
+ reason = "STATUS_TPM_NOTRESETABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOTLOCAL:
+ reason = "STATUS_TPM_NOTLOCAL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_TYPE:
+ reason = "STATUS_TPM_BAD_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_RESOURCE:
+ reason = "STATUS_TPM_INVALID_RESOURCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOTFIPS:
+ reason = "STATUS_TPM_NOTFIPS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_FAMILY:
+ reason = "STATUS_TPM_INVALID_FAMILY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NO_NV_PERMISSION:
+ reason = "STATUS_TPM_NO_NV_PERMISSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_REQUIRES_SIGN:
+ reason = "STATUS_TPM_REQUIRES_SIGN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_KEY_NOTSUPPORTED:
+ reason = "STATUS_TPM_KEY_NOTSUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AUTH_CONFLICT:
+ reason = "STATUS_TPM_AUTH_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_AREA_LOCKED:
+ reason = "STATUS_TPM_AREA_LOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_LOCALITY:
+ reason = "STATUS_TPM_BAD_LOCALITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_READ_ONLY:
+ reason = "STATUS_TPM_READ_ONLY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_PER_NOWRITE:
+ reason = "STATUS_TPM_PER_NOWRITE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_FAMILYCOUNT:
+ reason = "STATUS_TPM_FAMILYCOUNT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_WRITE_LOCKED:
+ reason = "STATUS_TPM_WRITE_LOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_ATTRIBUTES:
+ reason = "STATUS_TPM_BAD_ATTRIBUTES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_STRUCTURE:
+ reason = "STATUS_TPM_INVALID_STRUCTURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_KEY_OWNER_CONTROL:
+ reason = "STATUS_TPM_KEY_OWNER_CONTROL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_COUNTER:
+ reason = "STATUS_TPM_BAD_COUNTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOT_FULLWRITE:
+ reason = "STATUS_TPM_NOT_FULLWRITE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_CONTEXT_GAP:
+ reason = "STATUS_TPM_CONTEXT_GAP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MAXNVWRITES:
+ reason = "STATUS_TPM_MAXNVWRITES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOOPERATOR:
+ reason = "STATUS_TPM_NOOPERATOR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_RESOURCEMISSING:
+ reason = "STATUS_TPM_RESOURCEMISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_LOCK:
+ reason = "STATUS_TPM_DELEGATE_LOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_FAMILY:
+ reason = "STATUS_TPM_DELEGATE_FAMILY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_ADMIN:
+ reason = "STATUS_TPM_DELEGATE_ADMIN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_TRANSPORT_NOTEXCLUSIVE:
+ reason = "STATUS_TPM_TRANSPORT_NOTEXCLUSIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_OWNER_CONTROL:
+ reason = "STATUS_TPM_OWNER_CONTROL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_RESOURCES:
+ reason = "STATUS_TPM_DAA_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_INPUT_DATA0:
+ reason = "STATUS_TPM_DAA_INPUT_DATA0";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_INPUT_DATA1:
+ reason = "STATUS_TPM_DAA_INPUT_DATA1";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_ISSUER_SETTINGS:
+ reason = "STATUS_TPM_DAA_ISSUER_SETTINGS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_TPM_SETTINGS:
+ reason = "STATUS_TPM_DAA_TPM_SETTINGS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_STAGE:
+ reason = "STATUS_TPM_DAA_STAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_ISSUER_VALIDITY:
+ reason = "STATUS_TPM_DAA_ISSUER_VALIDITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DAA_WRONG_W:
+ reason = "STATUS_TPM_DAA_WRONG_W";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_HANDLE:
+ reason = "STATUS_TPM_BAD_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_DELEGATE:
+ reason = "STATUS_TPM_BAD_DELEGATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BADCONTEXT:
+ reason = "STATUS_TPM_BADCONTEXT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_TOOMANYCONTEXTS:
+ reason = "STATUS_TPM_TOOMANYCONTEXTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MA_TICKET_SIGNATURE:
+ reason = "STATUS_TPM_MA_TICKET_SIGNATURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MA_DESTINATION:
+ reason = "STATUS_TPM_MA_DESTINATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MA_SOURCE:
+ reason = "STATUS_TPM_MA_SOURCE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_MA_AUTHORITY:
+ reason = "STATUS_TPM_MA_AUTHORITY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_PERMANENTEK:
+ reason = "STATUS_TPM_PERMANENTEK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_BAD_SIGNATURE:
+ reason = "STATUS_TPM_BAD_SIGNATURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOCONTEXTSPACE:
+ reason = "STATUS_TPM_NOCONTEXTSPACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_COMMAND_BLOCKED:
+ reason = "STATUS_TPM_COMMAND_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INVALID_HANDLE:
+ reason = "STATUS_TPM_INVALID_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DUPLICATE_VHANDLE:
+ reason = "STATUS_TPM_DUPLICATE_VHANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_EMBEDDED_COMMAND_BLOCKED:
+ reason = "STATUS_TPM_EMBEDDED_COMMAND_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_EMBEDDED_COMMAND_UNSUPPORTED:
+ reason = "STATUS_TPM_EMBEDDED_COMMAND_UNSUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_RETRY:
+ reason = "STATUS_TPM_RETRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NEEDS_SELFTEST:
+ reason = "STATUS_TPM_NEEDS_SELFTEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DOING_SELFTEST:
+ reason = "STATUS_TPM_DOING_SELFTEST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_DEFEND_LOCK_RUNNING:
+ reason = "STATUS_TPM_DEFEND_LOCK_RUNNING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_COMMAND_CANCELED:
+ reason = "STATUS_TPM_COMMAND_CANCELED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_TOO_MANY_CONTEXTS:
+ reason = "STATUS_TPM_TOO_MANY_CONTEXTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_NOT_FOUND:
+ reason = "STATUS_TPM_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_ACCESS_DENIED:
+ reason = "STATUS_TPM_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_INSUFFICIENT_BUFFER:
+ reason = "STATUS_TPM_INSUFFICIENT_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_TPM_PPI_FUNCTION_UNSUPPORTED:
+ reason = "STATUS_TPM_PPI_FUNCTION_UNSUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_ERROR_MASK:
+ reason = "STATUS_PCP_ERROR_MASK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_DEVICE_NOT_READY:
+ reason = "STATUS_PCP_DEVICE_NOT_READY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_INVALID_HANDLE:
+ reason = "STATUS_PCP_INVALID_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_INVALID_PARAMETER:
+ reason = "STATUS_PCP_INVALID_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_FLAG_NOT_SUPPORTED:
+ reason = "STATUS_PCP_FLAG_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_NOT_SUPPORTED:
+ reason = "STATUS_PCP_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_BUFFER_TOO_SMALL:
+ reason = "STATUS_PCP_BUFFER_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_INTERNAL_ERROR:
+ reason = "STATUS_PCP_INTERNAL_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_AUTHENTICATION_FAILED:
+ reason = "STATUS_PCP_AUTHENTICATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_AUTHENTICATION_IGNORED:
+ reason = "STATUS_PCP_AUTHENTICATION_IGNORED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_POLICY_NOT_FOUND:
+ reason = "STATUS_PCP_POLICY_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_PROFILE_NOT_FOUND:
+ reason = "STATUS_PCP_PROFILE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_VALIDATION_FAILED:
+ reason = "STATUS_PCP_VALIDATION_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_PCP_DEVICE_NOT_FOUND:
+ reason = "STATUS_PCP_DEVICE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_HYPERCALL_CODE:
+ reason = "STATUS_HV_INVALID_HYPERCALL_CODE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_HYPERCALL_INPUT:
+ reason = "STATUS_HV_INVALID_HYPERCALL_INPUT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_ALIGNMENT:
+ reason = "STATUS_HV_INVALID_ALIGNMENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARAMETER:
+ reason = "STATUS_HV_INVALID_PARAMETER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_ACCESS_DENIED:
+ reason = "STATUS_HV_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARTITION_STATE:
+ reason = "STATUS_HV_INVALID_PARTITION_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_OPERATION_DENIED:
+ reason = "STATUS_HV_OPERATION_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_UNKNOWN_PROPERTY:
+ reason = "STATUS_HV_UNKNOWN_PROPERTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_PROPERTY_VALUE_OUT_OF_RANGE:
+ reason = "STATUS_HV_PROPERTY_VALUE_OUT_OF_RANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_MEMORY:
+ reason = "STATUS_HV_INSUFFICIENT_MEMORY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_PARTITION_TOO_DEEP:
+ reason = "STATUS_HV_PARTITION_TOO_DEEP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARTITION_ID:
+ reason = "STATUS_HV_INVALID_PARTITION_ID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_VP_INDEX:
+ reason = "STATUS_HV_INVALID_VP_INDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_PORT_ID:
+ reason = "STATUS_HV_INVALID_PORT_ID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_CONNECTION_ID:
+ reason = "STATUS_HV_INVALID_CONNECTION_ID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_BUFFERS:
+ reason = "STATUS_HV_INSUFFICIENT_BUFFERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_NOT_ACKNOWLEDGED:
+ reason = "STATUS_HV_NOT_ACKNOWLEDGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_ACKNOWLEDGED:
+ reason = "STATUS_HV_ACKNOWLEDGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_SAVE_RESTORE_STATE:
+ reason = "STATUS_HV_INVALID_SAVE_RESTORE_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_SYNIC_STATE:
+ reason = "STATUS_HV_INVALID_SYNIC_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_OBJECT_IN_USE:
+ reason = "STATUS_HV_OBJECT_IN_USE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_PROXIMITY_DOMAIN_INFO:
+ reason = "STATUS_HV_INVALID_PROXIMITY_DOMAIN_INFO";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_NO_DATA:
+ reason = "STATUS_HV_NO_DATA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INACTIVE:
+ reason = "STATUS_HV_INACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_NO_RESOURCES:
+ reason = "STATUS_HV_NO_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_FEATURE_UNAVAILABLE:
+ reason = "STATUS_HV_FEATURE_UNAVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_BUFFER:
+ reason = "STATUS_HV_INSUFFICIENT_BUFFER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_DEVICE_DOMAINS:
+ reason = "STATUS_HV_INSUFFICIENT_DEVICE_DOMAINS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_INVALID_LP_INDEX:
+ reason = "STATUS_HV_INVALID_LP_INDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HV_NOT_PRESENT:
+ reason = "STATUS_HV_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_BAD_SPI:
+ reason = "STATUS_IPSEC_BAD_SPI";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_SA_LIFETIME_EXPIRED:
+ reason = "STATUS_IPSEC_SA_LIFETIME_EXPIRED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_WRONG_SA:
+ reason = "STATUS_IPSEC_WRONG_SA";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_REPLAY_CHECK_FAILED:
+ reason = "STATUS_IPSEC_REPLAY_CHECK_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_INVALID_PACKET:
+ reason = "STATUS_IPSEC_INVALID_PACKET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_INTEGRITY_CHECK_FAILED:
+ reason = "STATUS_IPSEC_INTEGRITY_CHECK_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_CLEAR_TEXT_DROP:
+ reason = "STATUS_IPSEC_CLEAR_TEXT_DROP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_AUTH_FIREWALL_DROP:
+ reason = "STATUS_IPSEC_AUTH_FIREWALL_DROP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_THROTTLE_DROP:
+ reason = "STATUS_IPSEC_THROTTLE_DROP";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_BLOCK:
+ reason = "STATUS_IPSEC_DOSP_BLOCK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_RECEIVED_MULTICAST:
+ reason = "STATUS_IPSEC_DOSP_RECEIVED_MULTICAST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_INVALID_PACKET:
+ reason = "STATUS_IPSEC_DOSP_INVALID_PACKET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_STATE_LOOKUP_FAILED:
+ reason = "STATUS_IPSEC_DOSP_STATE_LOOKUP_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_MAX_ENTRIES:
+ reason = "STATUS_IPSEC_DOSP_MAX_ENTRIES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_KEYMOD_NOT_ALLOWED:
+ reason = "STATUS_IPSEC_DOSP_KEYMOD_NOT_ALLOWED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES:
+ reason = "STATUS_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_DUPLICATE_HANDLER:
+ reason = "STATUS_VID_DUPLICATE_HANDLER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_TOO_MANY_HANDLERS:
+ reason = "STATUS_VID_TOO_MANY_HANDLERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_QUEUE_FULL:
+ reason = "STATUS_VID_QUEUE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_HANDLER_NOT_PRESENT:
+ reason = "STATUS_VID_HANDLER_NOT_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_OBJECT_NAME:
+ reason = "STATUS_VID_INVALID_OBJECT_NAME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_PARTITION_NAME_TOO_LONG:
+ reason = "STATUS_VID_PARTITION_NAME_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_NAME_TOO_LONG:
+ reason = "STATUS_VID_MESSAGE_QUEUE_NAME_TOO_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_PARTITION_ALREADY_EXISTS:
+ reason = "STATUS_VID_PARTITION_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_PARTITION_DOES_NOT_EXIST:
+ reason = "STATUS_VID_PARTITION_DOES_NOT_EXIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_PARTITION_NAME_NOT_FOUND:
+ reason = "STATUS_VID_PARTITION_NAME_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_ALREADY_EXISTS:
+ reason = "STATUS_VID_MESSAGE_QUEUE_ALREADY_EXISTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_EXCEEDED_MBP_ENTRY_MAP_LIMIT:
+ reason = "STATUS_VID_EXCEEDED_MBP_ENTRY_MAP_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MB_STILL_REFERENCED:
+ reason = "STATUS_VID_MB_STILL_REFERENCED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_CHILD_GPA_PAGE_SET_CORRUPTED:
+ reason = "STATUS_VID_CHILD_GPA_PAGE_SET_CORRUPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_NUMA_SETTINGS:
+ reason = "STATUS_VID_INVALID_NUMA_SETTINGS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_NUMA_NODE_INDEX:
+ reason = "STATUS_VID_INVALID_NUMA_NODE_INDEX";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_NOTIFICATION_QUEUE_ALREADY_ASSOCIATED:
+ reason = "STATUS_VID_NOTIFICATION_QUEUE_ALREADY_ASSOCIATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_MEMORY_BLOCK_HANDLE:
+ reason = "STATUS_VID_INVALID_MEMORY_BLOCK_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_PAGE_RANGE_OVERFLOW:
+ reason = "STATUS_VID_PAGE_RANGE_OVERFLOW";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_MESSAGE_QUEUE_HANDLE:
+ reason = "STATUS_VID_INVALID_MESSAGE_QUEUE_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_GPA_RANGE_HANDLE:
+ reason = "STATUS_VID_INVALID_GPA_RANGE_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_NO_MEMORY_BLOCK_NOTIFICATION_QUEUE:
+ reason = "STATUS_VID_NO_MEMORY_BLOCK_NOTIFICATION_QUEUE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MEMORY_BLOCK_LOCK_COUNT_EXCEEDED:
+ reason = "STATUS_VID_MEMORY_BLOCK_LOCK_COUNT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_PPM_HANDLE:
+ reason = "STATUS_VID_INVALID_PPM_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MBPS_ARE_LOCKED:
+ reason = "STATUS_VID_MBPS_ARE_LOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_CLOSED:
+ reason = "STATUS_VID_MESSAGE_QUEUE_CLOSED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_VIRTUAL_PROCESSOR_LIMIT_EXCEEDED:
+ reason = "STATUS_VID_VIRTUAL_PROCESSOR_LIMIT_EXCEEDED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_STOP_PENDING:
+ reason = "STATUS_VID_STOP_PENDING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_PROCESSOR_STATE:
+ reason = "STATUS_VID_INVALID_PROCESSOR_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_EXCEEDED_KM_CONTEXT_COUNT_LIMIT:
+ reason = "STATUS_VID_EXCEEDED_KM_CONTEXT_COUNT_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_KM_INTERFACE_ALREADY_INITIALIZED:
+ reason = "STATUS_VID_KM_INTERFACE_ALREADY_INITIALIZED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MB_PROPERTY_ALREADY_SET_RESET:
+ reason = "STATUS_VID_MB_PROPERTY_ALREADY_SET_RESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MMIO_RANGE_DESTROYED:
+ reason = "STATUS_VID_MMIO_RANGE_DESTROYED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_INVALID_CHILD_GPA_PAGE_SET:
+ reason = "STATUS_VID_INVALID_CHILD_GPA_PAGE_SET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_RESERVE_PAGE_SET_IS_BEING_USED:
+ reason = "STATUS_VID_RESERVE_PAGE_SET_IS_BEING_USED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_RESERVE_PAGE_SET_TOO_SMALL:
+ reason = "STATUS_VID_RESERVE_PAGE_SET_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MBP_ALREADY_LOCKED_USING_RESERVED_PAGE:
+ reason = "STATUS_VID_MBP_ALREADY_LOCKED_USING_RESERVED_PAGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_MBP_COUNT_EXCEEDED_LIMIT:
+ reason = "STATUS_VID_MBP_COUNT_EXCEEDED_LIMIT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_CORRUPT:
+ reason = "STATUS_VID_SAVED_STATE_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_UNRECOGNIZED_ITEM:
+ reason = "STATUS_VID_SAVED_STATE_UNRECOGNIZED_ITEM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_INCOMPATIBLE:
+ reason = "STATUS_VID_SAVED_STATE_INCOMPATIBLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DATABASE_FULL:
+ reason = "STATUS_VOLMGR_DATABASE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONFIGURATION_CORRUPTED:
+ reason = "STATUS_VOLMGR_DISK_CONFIGURATION_CORRUPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONFIGURATION_NOT_IN_SYNC:
+ reason = "STATUS_VOLMGR_DISK_CONFIGURATION_NOT_IN_SYNC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_UPDATE_FAILED:
+ reason = "STATUS_VOLMGR_PACK_CONFIG_UPDATE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONTAINS_NON_SIMPLE_VOLUME:
+ reason = "STATUS_VOLMGR_DISK_CONTAINS_NON_SIMPLE_VOLUME";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_DUPLICATE:
+ reason = "STATUS_VOLMGR_DISK_DUPLICATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_DYNAMIC:
+ reason = "STATUS_VOLMGR_DISK_DYNAMIC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_ID_INVALID:
+ reason = "STATUS_VOLMGR_DISK_ID_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_INVALID:
+ reason = "STATUS_VOLMGR_DISK_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAST_VOTER:
+ reason = "STATUS_VOLMGR_DISK_LAST_VOTER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_INVALID:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_NON_BASIC_BETWEEN_BASIC_PARTITIONS:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_NON_BASIC_BETWEEN_BASIC_PARTITIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_NOT_CYLINDER_ALIGNED:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_NOT_CYLINDER_ALIGNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_PARTITIONS_TOO_SMALL:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_PARTITIONS_TOO_SMALL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_PRIMARY_BETWEEN_LOGICAL_PARTITIONS:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_PRIMARY_BETWEEN_LOGICAL_PARTITIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_TOO_MANY_PARTITIONS:
+ reason = "STATUS_VOLMGR_DISK_LAYOUT_TOO_MANY_PARTITIONS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_MISSING:
+ reason = "STATUS_VOLMGR_DISK_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_NOT_EMPTY:
+ reason = "STATUS_VOLMGR_DISK_NOT_EMPTY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_NOT_ENOUGH_SPACE:
+ reason = "STATUS_VOLMGR_DISK_NOT_ENOUGH_SPACE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_REVECTORING_FAILED:
+ reason = "STATUS_VOLMGR_DISK_REVECTORING_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_SECTOR_SIZE_INVALID:
+ reason = "STATUS_VOLMGR_DISK_SECTOR_SIZE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_SET_NOT_CONTAINED:
+ reason = "STATUS_VOLMGR_DISK_SET_NOT_CONTAINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_MEMBERS:
+ reason = "STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_MEMBERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_PLEXES:
+ reason = "STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_PLEXES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DYNAMIC_DISK_NOT_SUPPORTED:
+ reason = "STATUS_VOLMGR_DYNAMIC_DISK_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_ALREADY_USED:
+ reason = "STATUS_VOLMGR_EXTENT_ALREADY_USED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_CONTIGUOUS:
+ reason = "STATUS_VOLMGR_EXTENT_NOT_CONTIGUOUS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_IN_PUBLIC_REGION:
+ reason = "STATUS_VOLMGR_EXTENT_NOT_IN_PUBLIC_REGION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_SECTOR_ALIGNED:
+ reason = "STATUS_VOLMGR_EXTENT_NOT_SECTOR_ALIGNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_OVERLAPS_EBR_PARTITION:
+ reason = "STATUS_VOLMGR_EXTENT_OVERLAPS_EBR_PARTITION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_VOLUME_LENGTHS_DO_NOT_MATCH:
+ reason = "STATUS_VOLMGR_EXTENT_VOLUME_LENGTHS_DO_NOT_MATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_FAULT_TOLERANT_NOT_SUPPORTED:
+ reason = "STATUS_VOLMGR_FAULT_TOLERANT_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_INTERLEAVE_LENGTH_INVALID:
+ reason = "STATUS_VOLMGR_INTERLEAVE_LENGTH_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MAXIMUM_REGISTERED_USERS:
+ reason = "STATUS_VOLMGR_MAXIMUM_REGISTERED_USERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_IN_SYNC:
+ reason = "STATUS_VOLMGR_MEMBER_IN_SYNC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_INDEX_DUPLICATE:
+ reason = "STATUS_VOLMGR_MEMBER_INDEX_DUPLICATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_INDEX_INVALID:
+ reason = "STATUS_VOLMGR_MEMBER_INDEX_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_MISSING:
+ reason = "STATUS_VOLMGR_MEMBER_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_NOT_DETACHED:
+ reason = "STATUS_VOLMGR_MEMBER_NOT_DETACHED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_REGENERATING:
+ reason = "STATUS_VOLMGR_MEMBER_REGENERATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_ALL_DISKS_FAILED:
+ reason = "STATUS_VOLMGR_ALL_DISKS_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_REGISTERED_USERS:
+ reason = "STATUS_VOLMGR_NO_REGISTERED_USERS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_SUCH_USER:
+ reason = "STATUS_VOLMGR_NO_SUCH_USER";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NOTIFICATION_RESET:
+ reason = "STATUS_VOLMGR_NOTIFICATION_RESET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_MEMBERS_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_MEMBERS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_PLEXES_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_PLEXES_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_DUPLICATE:
+ reason = "STATUS_VOLMGR_PACK_DUPLICATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_ID_INVALID:
+ reason = "STATUS_VOLMGR_PACK_ID_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_INVALID:
+ reason = "STATUS_VOLMGR_PACK_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_NAME_INVALID:
+ reason = "STATUS_VOLMGR_PACK_NAME_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_OFFLINE:
+ reason = "STATUS_VOLMGR_PACK_OFFLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_HAS_QUORUM:
+ reason = "STATUS_VOLMGR_PACK_HAS_QUORUM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_WITHOUT_QUORUM:
+ reason = "STATUS_VOLMGR_PACK_WITHOUT_QUORUM";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PARTITION_STYLE_INVALID:
+ reason = "STATUS_VOLMGR_PARTITION_STYLE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PARTITION_UPDATE_FAILED:
+ reason = "STATUS_VOLMGR_PARTITION_UPDATE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_IN_SYNC:
+ reason = "STATUS_VOLMGR_PLEX_IN_SYNC";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_INDEX_DUPLICATE:
+ reason = "STATUS_VOLMGR_PLEX_INDEX_DUPLICATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_INDEX_INVALID:
+ reason = "STATUS_VOLMGR_PLEX_INDEX_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_LAST_ACTIVE:
+ reason = "STATUS_VOLMGR_PLEX_LAST_ACTIVE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_MISSING:
+ reason = "STATUS_VOLMGR_PLEX_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_REGENERATING:
+ reason = "STATUS_VOLMGR_PLEX_REGENERATING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_TYPE_INVALID:
+ reason = "STATUS_VOLMGR_PLEX_TYPE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_RAID5:
+ reason = "STATUS_VOLMGR_PLEX_NOT_RAID5";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_SIMPLE:
+ reason = "STATUS_VOLMGR_PLEX_NOT_SIMPLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_STRUCTURE_SIZE_INVALID:
+ reason = "STATUS_VOLMGR_STRUCTURE_SIZE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_TOO_MANY_NOTIFICATION_REQUESTS:
+ reason = "STATUS_VOLMGR_TOO_MANY_NOTIFICATION_REQUESTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_TRANSACTION_IN_PROGRESS:
+ reason = "STATUS_VOLMGR_TRANSACTION_IN_PROGRESS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_UNEXPECTED_DISK_LAYOUT_CHANGE:
+ reason = "STATUS_VOLMGR_UNEXPECTED_DISK_LAYOUT_CHANGE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_CONTAINS_MISSING_DISK:
+ reason = "STATUS_VOLMGR_VOLUME_CONTAINS_MISSING_DISK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_ID_INVALID:
+ reason = "STATUS_VOLMGR_VOLUME_ID_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_LENGTH_INVALID:
+ reason = "STATUS_VOLMGR_VOLUME_LENGTH_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_LENGTH_NOT_SECTOR_SIZE_MULTIPLE:
+ reason = "STATUS_VOLMGR_VOLUME_LENGTH_NOT_SECTOR_SIZE_MULTIPLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_NOT_MIRRORED:
+ reason = "STATUS_VOLMGR_VOLUME_NOT_MIRRORED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_NOT_RETAINED:
+ reason = "STATUS_VOLMGR_VOLUME_NOT_RETAINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_OFFLINE:
+ reason = "STATUS_VOLMGR_VOLUME_OFFLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_RETAINED:
+ reason = "STATUS_VOLMGR_VOLUME_RETAINED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_EXTENTS_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_EXTENTS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_DIFFERENT_SECTOR_SIZE:
+ reason = "STATUS_VOLMGR_DIFFERENT_SECTOR_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_BAD_BOOT_DISK:
+ reason = "STATUS_VOLMGR_BAD_BOOT_DISK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_OFFLINE:
+ reason = "STATUS_VOLMGR_PACK_CONFIG_OFFLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_ONLINE:
+ reason = "STATUS_VOLMGR_PACK_CONFIG_ONLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NOT_PRIMARY_PACK:
+ reason = "STATUS_VOLMGR_NOT_PRIMARY_PACK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_LOG_UPDATE_FAILED:
+ reason = "STATUS_VOLMGR_PACK_LOG_UPDATE_FAILED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_IN_PLEX_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_DISKS_IN_PLEX_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_IN_MEMBER_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_DISKS_IN_MEMBER_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_MIRRORED:
+ reason = "STATUS_VOLMGR_VOLUME_MIRRORED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_SIMPLE_SPANNED:
+ reason = "STATUS_VOLMGR_PLEX_NOT_SIMPLE_SPANNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_VALID_LOG_COPIES:
+ reason = "STATUS_VOLMGR_NO_VALID_LOG_COPIES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_PRIMARY_PACK_PRESENT:
+ reason = "STATUS_VOLMGR_PRIMARY_PACK_PRESENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_INVALID:
+ reason = "STATUS_VOLMGR_NUMBER_OF_DISKS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_MIRROR_NOT_SUPPORTED:
+ reason = "STATUS_VOLMGR_MIRROR_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLMGR_RAID5_NOT_SUPPORTED:
+ reason = "STATUS_VOLMGR_RAID5_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BCD_TOO_MANY_ELEMENTS:
+ reason = "STATUS_BCD_TOO_MANY_ELEMENTS";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_MISSING:
+ reason = "STATUS_VHD_DRIVE_FOOTER_MISSING";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_CHECKSUM_MISMATCH:
+ reason = "STATUS_VHD_DRIVE_FOOTER_CHECKSUM_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_CORRUPT:
+ reason = "STATUS_VHD_DRIVE_FOOTER_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_FORMAT_UNKNOWN:
+ reason = "STATUS_VHD_FORMAT_UNKNOWN";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_FORMAT_UNSUPPORTED_VERSION:
+ reason = "STATUS_VHD_FORMAT_UNSUPPORTED_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_CHECKSUM_MISMATCH:
+ reason = "STATUS_VHD_SPARSE_HEADER_CHECKSUM_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_UNSUPPORTED_VERSION:
+ reason = "STATUS_VHD_SPARSE_HEADER_UNSUPPORTED_VERSION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_CORRUPT:
+ reason = "STATUS_VHD_SPARSE_HEADER_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_BLOCK_ALLOCATION_FAILURE:
+ reason = "STATUS_VHD_BLOCK_ALLOCATION_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_BLOCK_ALLOCATION_TABLE_CORRUPT:
+ reason = "STATUS_VHD_BLOCK_ALLOCATION_TABLE_CORRUPT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_INVALID_BLOCK_SIZE:
+ reason = "STATUS_VHD_INVALID_BLOCK_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_BITMAP_MISMATCH:
+ reason = "STATUS_VHD_BITMAP_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_PARENT_VHD_NOT_FOUND:
+ reason = "STATUS_VHD_PARENT_VHD_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_ID_MISMATCH:
+ reason = "STATUS_VHD_CHILD_PARENT_ID_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_TIMESTAMP_MISMATCH:
+ reason = "STATUS_VHD_CHILD_PARENT_TIMESTAMP_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_METADATA_READ_FAILURE:
+ reason = "STATUS_VHD_METADATA_READ_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_METADATA_WRITE_FAILURE:
+ reason = "STATUS_VHD_METADATA_WRITE_FAILURE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_INVALID_SIZE:
+ reason = "STATUS_VHD_INVALID_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_INVALID_FILE_SIZE:
+ reason = "STATUS_VHD_INVALID_FILE_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTDISK_PROVIDER_NOT_FOUND:
+ reason = "STATUS_VIRTDISK_PROVIDER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTDISK_NOT_VIRTUAL_DISK:
+ reason = "STATUS_VIRTDISK_NOT_VIRTUAL_DISK";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_PARENT_VHD_ACCESS_DENIED:
+ reason = "STATUS_VHD_PARENT_VHD_ACCESS_DENIED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_SIZE_MISMATCH:
+ reason = "STATUS_VHD_CHILD_PARENT_SIZE_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED:
+ reason = "STATUS_VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT:
+ reason = "STATUS_VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTUAL_DISK_LIMITATION:
+ reason = "STATUS_VIRTUAL_DISK_LIMITATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_INVALID_TYPE:
+ reason = "STATUS_VHD_INVALID_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_INVALID_STATE:
+ reason = "STATUS_VHD_INVALID_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTDISK_UNSUPPORTED_DISK_SECTOR_SIZE:
+ reason = "STATUS_VIRTDISK_UNSUPPORTED_DISK_SECTOR_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTDISK_DISK_ALREADY_OWNED:
+ reason = "STATUS_VIRTDISK_DISK_ALREADY_OWNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VIRTDISK_DISK_ONLINE_AND_WRITABLE:
+ reason = "STATUS_VIRTDISK_DISK_ONLINE_AND_WRITABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTLOG_TRACKING_NOT_INITIALIZED:
+ reason = "STATUS_CTLOG_TRACKING_NOT_INITIALIZED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTLOG_LOGFILE_SIZE_EXCEEDED_MAXSIZE:
+ reason = "STATUS_CTLOG_LOGFILE_SIZE_EXCEEDED_MAXSIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTLOG_VHD_CHANGED_OFFLINE:
+ reason = "STATUS_CTLOG_VHD_CHANGED_OFFLINE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTLOG_INVALID_TRACKING_STATE:
+ reason = "STATUS_CTLOG_INVALID_TRACKING_STATE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_CTLOG_INCONSISTENT_TRACKING_FILE:
+ reason = "STATUS_CTLOG_INCONSISTENT_TRACKING_FILE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_METADATA_FULL:
+ reason = "STATUS_VHD_METADATA_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_KEY_NOT_FOUND:
+ reason = "STATUS_RKF_KEY_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_DUPLICATE_KEY:
+ reason = "STATUS_RKF_DUPLICATE_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_BLOB_FULL:
+ reason = "STATUS_RKF_BLOB_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_STORE_FULL:
+ reason = "STATUS_RKF_STORE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_FILE_BLOCKED:
+ reason = "STATUS_RKF_FILE_BLOCKED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RKF_ACTIVE_KEY:
+ reason = "STATUS_RKF_ACTIVE_KEY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RDBSS_RESTART_OPERATION:
+ reason = "STATUS_RDBSS_RESTART_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RDBSS_CONTINUE_OPERATION:
+ reason = "STATUS_RDBSS_CONTINUE_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_RDBSS_POST_OPERATION:
+ reason = "STATUS_RDBSS_POST_OPERATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_HANDLE:
+ reason = "STATUS_BTH_ATT_INVALID_HANDLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_READ_NOT_PERMITTED:
+ reason = "STATUS_BTH_ATT_READ_NOT_PERMITTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_WRITE_NOT_PERMITTED:
+ reason = "STATUS_BTH_ATT_WRITE_NOT_PERMITTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_PDU:
+ reason = "STATUS_BTH_ATT_INVALID_PDU";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_AUTHENTICATION:
+ reason = "STATUS_BTH_ATT_INSUFFICIENT_AUTHENTICATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_REQUEST_NOT_SUPPORTED:
+ reason = "STATUS_BTH_ATT_REQUEST_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_OFFSET:
+ reason = "STATUS_BTH_ATT_INVALID_OFFSET";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_AUTHORIZATION:
+ reason = "STATUS_BTH_ATT_INSUFFICIENT_AUTHORIZATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_PREPARE_QUEUE_FULL:
+ reason = "STATUS_BTH_ATT_PREPARE_QUEUE_FULL";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_ATTRIBUTE_NOT_FOUND:
+ reason = "STATUS_BTH_ATT_ATTRIBUTE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_ATTRIBUTE_NOT_LONG:
+ reason = "STATUS_BTH_ATT_ATTRIBUTE_NOT_LONG";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+ reason = "STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH:
+ reason = "STATUS_BTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNLIKELY:
+ reason = "STATUS_BTH_ATT_UNLIKELY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION:
+ reason = "STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNSUPPORTED_GROUP_TYPE:
+ reason = "STATUS_BTH_ATT_UNSUPPORTED_GROUP_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_RESOURCES:
+ reason = "STATUS_BTH_ATT_INSUFFICIENT_RESOURCES";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNKNOWN_ERROR:
+ reason = "STATUS_BTH_ATT_UNKNOWN_ERROR";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_ROLLBACK_DETECTED:
+ reason = "STATUS_SECUREBOOT_ROLLBACK_DETECTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_VIOLATION:
+ reason = "STATUS_SECUREBOOT_POLICY_VIOLATION";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_INVALID_POLICY:
+ reason = "STATUS_SECUREBOOT_INVALID_POLICY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_PUBLISHER_NOT_FOUND:
+ reason = "STATUS_SECUREBOOT_POLICY_PUBLISHER_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_NOT_SIGNED:
+ reason = "STATUS_SECUREBOOT_POLICY_NOT_SIGNED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SECUREBOOT_FILE_REPLACED:
+ reason = "STATUS_SECUREBOOT_FILE_REPLACED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_AUDIO_ENGINE_NODE_NOT_FOUND:
+ reason = "STATUS_AUDIO_ENGINE_NODE_NOT_FOUND";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HDAUDIO_EMPTY_CONNECTION_LIST:
+ reason = "STATUS_HDAUDIO_EMPTY_CONNECTION_LIST";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HDAUDIO_CONNECTION_LIST_NOT_SUPPORTED:
+ reason = "STATUS_HDAUDIO_CONNECTION_LIST_NOT_SUPPORTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HDAUDIO_NO_LOGICAL_DEVICES_CREATED:
+ reason = "STATUS_HDAUDIO_NO_LOGICAL_DEVICES_CREATED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_HDAUDIO_NULL_LINKED_LIST_ENTRY:
+ reason = "STATUS_HDAUDIO_NULL_LINKED_LIST_ENTRY";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VOLSNAP_BOOTFILE_NOT_VALID:
+ reason = "STATUS_VOLSNAP_BOOTFILE_NOT_VALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_IO_PREEMPTED:
+ reason = "STATUS_IO_PREEMPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_ERROR_STORED:
+ reason = "STATUS_SVHDX_ERROR_STORED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_ERROR_NOT_AVAILABLE:
+ reason = "STATUS_SVHDX_ERROR_NOT_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_AVAILABLE:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_AVAILABLE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_CAPACITY_DATA_CHANGED:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_CAPACITY_DATA_CHANGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_PREEMPTED:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_PREEMPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_RELEASED:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_RELEASED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_REGISTRATIONS_PREEMPTED:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_REGISTRATIONS_PREEMPTED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_OPERATING_DEFINITION_CHANGED:
+ reason = "STATUS_SVHDX_UNIT_ATTENTION_OPERATING_DEFINITION_CHANGED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_RESERVATION_CONFLICT:
+ reason = "STATUS_SVHDX_RESERVATION_CONFLICT";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_WRONG_FILE_TYPE:
+ reason = "STATUS_SVHDX_WRONG_FILE_TYPE";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SVHDX_VERSION_MISMATCH:
+ reason = "STATUS_SVHDX_VERSION_MISMATCH";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_VHD_SHARED:
+ reason = "STATUS_VHD_SHARED";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPACES_RESILIENCY_TYPE_INVALID:
+ reason = "STATUS_SPACES_RESILIENCY_TYPE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPACES_DRIVE_SECTOR_SIZE_INVALID:
+ reason = "STATUS_SPACES_DRIVE_SECTOR_SIZE_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPACES_INTERLEAVE_LENGTH_INVALID:
+ reason = "STATUS_SPACES_INTERLEAVE_LENGTH_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPACES_NUMBER_OF_COLUMNS_INVALID:
+ reason = "STATUS_SPACES_NUMBER_OF_COLUMNS_INVALID";
+ break;
+ case MD_NTSTATUS_WIN_STATUS_SPACES_NOT_ENOUGH_DRIVES:
+ reason = "STATUS_SPACES_NOT_ENOUGH_DRIVES";
+ break;
+ default: {
+ char reason_string[11];
+ snprintf(reason_string, sizeof(reason_string), "0x%08x", ntstatus);
+ reason = reason_string;
+ break;
+ }
+ }
+ return reason;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.h b/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.h
new file mode 100644
index 000000000..c05c91698
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/symbolic_constants_win.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2015 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ntstatus_reason_win.h: Windows NTSTATUS code to string.
+//
+// Provides a means to convert NTSTATUS codes to strings.
+//
+// Author: Ben Wagner
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYMBOLIC_CONSTANTS_WIN_H_
+#define GOOGLE_BREAKPAD_PROCESSOR_SYMBOLIC_CONSTANTS_WIN_H_
+
+#include <string>
+
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+/* Converts a NTSTATUS code to a reason string. */
+std::string NTStatusToString(uint32_t ntstatus);
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_PROCESSOR_SYMBOLIC_CONSTANTS_WIN_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc
new file mode 100644
index 000000000..2cfbb0888
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc
@@ -0,0 +1,391 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// synth_minidump.cc: Implementation of SynthMinidump. See synth_minidump.h
+
+#include "processor/synth_minidump.h"
+
+namespace google_breakpad {
+
+namespace SynthMinidump {
+
+Section::Section(const Dump &dump)
+ : test_assembler::Section(dump.endianness()) { }
+
+void Section::CiteLocationIn(test_assembler::Section *section) const {
+ if (this)
+ (*section).D32(size_).D32(file_offset_);
+ else
+ (*section).D32(0).D32(0);
+}
+
+void Stream::CiteStreamIn(test_assembler::Section *section) const {
+ section->D32(type_);
+ CiteLocationIn(section);
+}
+
+SystemInfo::SystemInfo(const Dump &dump,
+ const MDRawSystemInfo &system_info,
+ const String &csd_version)
+ : Stream(dump, MD_SYSTEM_INFO_STREAM) {
+ D16(system_info.processor_architecture);
+ D16(system_info.processor_level);
+ D16(system_info.processor_revision);
+ D8(system_info.number_of_processors);
+ D8(system_info.product_type);
+ D32(system_info.major_version);
+ D32(system_info.minor_version);
+ D32(system_info.build_number);
+ D32(system_info.platform_id);
+ csd_version.CiteStringIn(this);
+ D16(system_info.suite_mask);
+ D16(system_info.reserved2); // Well, why not?
+
+ // MDCPUInformation cpu;
+ if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_X86) {
+ D32(system_info.cpu.x86_cpu_info.vendor_id[0]);
+ D32(system_info.cpu.x86_cpu_info.vendor_id[1]);
+ D32(system_info.cpu.x86_cpu_info.vendor_id[2]);
+ D32(system_info.cpu.x86_cpu_info.version_information);
+ D32(system_info.cpu.x86_cpu_info.feature_information);
+ D32(system_info.cpu.x86_cpu_info.amd_extended_cpu_features);
+ } else if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_ARM) {
+ D32(system_info.cpu.arm_cpu_info.cpuid);
+ D32(system_info.cpu.arm_cpu_info.elf_hwcaps);
+ } else {
+ D64(system_info.cpu.other_cpu_info.processor_features[0]);
+ D64(system_info.cpu.other_cpu_info.processor_features[1]);
+ }
+}
+
+const MDRawSystemInfo SystemInfo::windows_x86 = {
+ MD_CPU_ARCHITECTURE_X86, // processor_architecture
+ 6, // processor_level
+ 0xd08, // processor_revision
+ 1, // number_of_processors
+ 1, // product_type
+ 5, // major_version
+ 1, // minor_version
+ 2600, // build_number
+ 2, // platform_id
+ 0xdeadbeef, // csd_version_rva
+ 0x100, // suite_mask
+ 0, // reserved2
+ { // cpu
+ { // x86_cpu_info
+ { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
+ 0x6d8, // version_information
+ 0xafe9fbff, // feature_information
+ 0xffffffff // amd_extended_cpu_features
+ }
+ }
+};
+
+const string SystemInfo::windows_x86_csd_version = "Service Pack 2";
+
+String::String(const Dump &dump, const string &contents) : Section(dump) {
+ D32(contents.size() * 2);
+ for (string::const_iterator i = contents.begin(); i != contents.end(); i++)
+ D16(*i);
+}
+
+void String::CiteStringIn(test_assembler::Section *section) const {
+ section->D32(file_offset_);
+}
+
+void Memory::CiteMemoryIn(test_assembler::Section *section) const {
+ section->D64(address_);
+ CiteLocationIn(section);
+}
+
+Context::Context(const Dump &dump, const MDRawContextX86 &context)
+ : Section(dump) {
+ // The caller should have properly set the CPU type flag.
+ // The high 24 bits identify the CPU. Note that context records with no CPU
+ // type information can be valid (e.g. produced by ::RtlCaptureContext).
+ assert(((context.context_flags & MD_CONTEXT_CPU_MASK) == 0) ||
+ (context.context_flags & MD_CONTEXT_X86));
+ // It doesn't make sense to store x86 registers in big-endian form.
+ assert(dump.endianness() == kLittleEndian);
+ D32(context.context_flags);
+ D32(context.dr0);
+ D32(context.dr1);
+ D32(context.dr2);
+ D32(context.dr3);
+ D32(context.dr6);
+ D32(context.dr7);
+ D32(context.float_save.control_word);
+ D32(context.float_save.status_word);
+ D32(context.float_save.tag_word);
+ D32(context.float_save.error_offset);
+ D32(context.float_save.error_selector);
+ D32(context.float_save.data_offset);
+ D32(context.float_save.data_selector);
+ // context.float_save.register_area[] contains 8-bit quantities and
+ // does not need to be swapped.
+ Append(context.float_save.register_area,
+ sizeof(context.float_save.register_area));
+ D32(context.float_save.cr0_npx_state);
+ D32(context.gs);
+ D32(context.fs);
+ D32(context.es);
+ D32(context.ds);
+ D32(context.edi);
+ D32(context.esi);
+ D32(context.ebx);
+ D32(context.edx);
+ D32(context.ecx);
+ D32(context.eax);
+ D32(context.ebp);
+ D32(context.eip);
+ D32(context.cs);
+ D32(context.eflags);
+ D32(context.esp);
+ D32(context.ss);
+ // context.extended_registers[] contains 8-bit quantities and does
+ // not need to be swapped.
+ Append(context.extended_registers, sizeof(context.extended_registers));
+ assert(Size() == sizeof(MDRawContextX86));
+}
+
+Context::Context(const Dump &dump, const MDRawContextARM &context)
+ : Section(dump) {
+ // The caller should have properly set the CPU type flag.
+ assert((context.context_flags & MD_CONTEXT_ARM) ||
+ (context.context_flags & MD_CONTEXT_ARM_OLD));
+ // It doesn't make sense to store ARM registers in big-endian form.
+ assert(dump.endianness() == kLittleEndian);
+ D32(context.context_flags);
+ for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
+ D32(context.iregs[i]);
+ D32(context.cpsr);
+ D64(context.float_save.fpscr);
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; ++i)
+ D64(context.float_save.regs[i]);
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; ++i)
+ D32(context.float_save.extra[i]);
+ assert(Size() == sizeof(MDRawContextARM));
+}
+
+Context::Context(const Dump &dump, const MDRawContextMIPS &context)
+ : Section(dump) {
+ // The caller should have properly set the CPU type flag.
+ assert(context.context_flags & MD_CONTEXT_MIPS);
+ D32(context.context_flags);
+ D32(context._pad0);
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
+ D64(context.iregs[i]);
+
+ D64(context.mdhi);
+ D64(context.mdlo);
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i)
+ D32(context.hi[i]);
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i)
+ D32(context.lo[i]);
+
+ D32(context.dsp_control);
+ D32(context._pad1);
+
+ D64(context.epc);
+ D64(context.badvaddr);
+ D32(context.status);
+ D32(context.cause);
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
+ D64(context.float_save.regs[i]);
+
+ D32(context.float_save.fpcsr);
+ D32(context.float_save.fir);
+
+ assert(Size() == sizeof(MDRawContextMIPS));
+}
+
+Thread::Thread(const Dump &dump,
+ uint32_t thread_id, const Memory &stack, const Context &context,
+ uint32_t suspend_count, uint32_t priority_class,
+ uint32_t priority, uint64_t teb) : Section(dump) {
+ D32(thread_id);
+ D32(suspend_count);
+ D32(priority_class);
+ D32(priority);
+ D64(teb);
+ stack.CiteMemoryIn(this);
+ context.CiteLocationIn(this);
+ assert(Size() == sizeof(MDRawThread));
+}
+
+Module::Module(const Dump &dump,
+ uint64_t base_of_image,
+ uint32_t size_of_image,
+ const String &name,
+ uint32_t time_date_stamp,
+ uint32_t checksum,
+ const MDVSFixedFileInfo &version_info,
+ const Section *cv_record,
+ const Section *misc_record) : Section(dump) {
+ D64(base_of_image);
+ D32(size_of_image);
+ D32(checksum);
+ D32(time_date_stamp);
+ name.CiteStringIn(this);
+ D32(version_info.signature);
+ D32(version_info.struct_version);
+ D32(version_info.file_version_hi);
+ D32(version_info.file_version_lo);
+ D32(version_info.product_version_hi);
+ D32(version_info.product_version_lo);
+ D32(version_info.file_flags_mask);
+ D32(version_info.file_flags);
+ D32(version_info.file_os);
+ D32(version_info.file_type);
+ D32(version_info.file_subtype);
+ D32(version_info.file_date_hi);
+ D32(version_info.file_date_lo);
+ cv_record->CiteLocationIn(this);
+ misc_record->CiteLocationIn(this);
+ D64(0).D64(0);
+}
+
+const MDVSFixedFileInfo Module::stock_version_info = {
+ MD_VSFIXEDFILEINFO_SIGNATURE, // signature
+ MD_VSFIXEDFILEINFO_VERSION, // struct_version
+ 0x11111111, // file_version_hi
+ 0x22222222, // file_version_lo
+ 0x33333333, // product_version_hi
+ 0x44444444, // product_version_lo
+ MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags_mask
+ MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags
+ MD_VSFIXEDFILEINFO_FILE_OS_NT | MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32,
+ // file_os
+ MD_VSFIXEDFILEINFO_FILE_TYPE_APP, // file_type
+ MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN, // file_subtype
+ 0, // file_date_hi
+ 0 // file_date_lo
+};
+
+Exception::Exception(const Dump &dump,
+ const Context &context,
+ uint32_t thread_id,
+ uint32_t exception_code,
+ uint32_t exception_flags,
+ uint64_t exception_address)
+ : Stream(dump, MD_EXCEPTION_STREAM) {
+ D32(thread_id);
+ D32(0); // __align
+ D32(exception_code);
+ D32(exception_flags);
+ D64(0); // exception_record
+ D64(exception_address);
+ D32(0); // number_parameters
+ D32(0); // __align
+ for (int i = 0; i < MD_EXCEPTION_MAXIMUM_PARAMETERS; ++i)
+ D64(0); // exception_information
+ context.CiteLocationIn(this);
+ assert(Size() == sizeof(MDRawExceptionStream));
+}
+
+Dump::Dump(uint64_t flags,
+ Endianness endianness,
+ uint32_t version,
+ uint32_t date_time_stamp)
+ : test_assembler::Section(endianness),
+ file_start_(0),
+ stream_directory_(*this),
+ stream_count_(0),
+ thread_list_(*this, MD_THREAD_LIST_STREAM),
+ module_list_(*this, MD_MODULE_LIST_STREAM),
+ memory_list_(*this, MD_MEMORY_LIST_STREAM)
+ {
+ D32(MD_HEADER_SIGNATURE);
+ D32(version);
+ D32(stream_count_label_);
+ D32(stream_directory_rva_);
+ D32(0);
+ D32(date_time_stamp);
+ D64(flags);
+ assert(Size() == sizeof(MDRawHeader));
+}
+
+Dump &Dump::Add(SynthMinidump::Section *section) {
+ section->Finish(file_start_ + Size());
+ Append(*section);
+ return *this;
+}
+
+Dump &Dump::Add(Stream *stream) {
+ Add(static_cast<SynthMinidump::Section *>(stream));
+ stream->CiteStreamIn(&stream_directory_);
+ stream_count_++;
+ return *this;
+}
+
+Dump &Dump::Add(Memory *memory) {
+ // Add the memory contents themselves to the file.
+ Add(static_cast<SynthMinidump::Section *>(memory));
+
+ // The memory list is a list of MDMemoryDescriptors, not of actual
+ // memory elements. Produce a descriptor, and add that to the list.
+ SynthMinidump::Section descriptor(*this);
+ memory->CiteMemoryIn(&descriptor);
+ memory_list_.Add(&descriptor);
+ return *this;
+}
+
+Dump &Dump::Add(Thread *thread) {
+ thread_list_.Add(thread);
+ return *this;
+}
+
+Dump &Dump::Add(Module *module) {
+ module_list_.Add(module);
+ return *this;
+}
+
+void Dump::Finish() {
+ if (!thread_list_.Empty()) Add(&thread_list_);
+ if (!module_list_.Empty()) Add(&module_list_);
+ if (!memory_list_.Empty()) Add(&memory_list_);
+
+ // Create the stream directory. We don't use
+ // stream_directory_.Finish here, because the stream directory isn't
+ // cited using a location descriptor; rather, the Minidump header
+ // has the stream count and MDRVA.
+ stream_count_label_ = stream_count_;
+ stream_directory_rva_ = file_start_ + Size();
+ Append(static_cast<test_assembler::Section &>(stream_directory_));
+}
+
+} // namespace SynthMinidump
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h
new file mode 100644
index 000000000..8dac8784e
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h
@@ -0,0 +1,372 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// synth_minidump.h: Interface to SynthMinidump: fake minidump generator.
+//
+// We treat a minidump file as the concatenation of a bunch of
+// test_assembler::Sections. The file header, stream directory,
+// streams, memory regions, strings, and so on --- each is a Section
+// that eventually gets appended to the minidump. Dump, Memory,
+// Context, Thread, and so on all inherit from test_assembler::Section.
+// For example:
+//
+// using google_breakpad::test_assembler::kLittleEndian;
+// using google_breakpad::SynthMinidump::Context;
+// using google_breakpad::SynthMinidump::Dump;
+// using google_breakpad::SynthMinidump::Memory;
+// using google_breakpad::SynthMinidump::Thread;
+//
+// Dump minidump(MD_NORMAL, kLittleEndian);
+//
+// Memory stack1(minidump, 0x569eb0a9);
+// ... build contents of stack1 with test_assembler::Section functions ...
+//
+// MDRawContextX86 x86_context1;
+// x86_context1.context_flags = MD_CONTEXT_X86;
+// x86_context1.eip = 0x7c90eb94;
+// x86_context1.esp = 0x569eb0a9;
+// x86_context1.ebp = x86_context1.esp + something appropriate;
+// Context context1(minidump, x86_context1);
+//
+// Thread thread1(minidump, 0xe4a4821d, stack1, context1);
+//
+// minidump.Add(&stack1);
+// minidump.Add(&context1);
+// minidump.Add(&thread1);
+// minidump.Finish();
+//
+// string contents;
+// EXPECT_TRUE(minidump.GetContents(&contents));
+// // contents now holds the bytes of a minidump file
+//
+// Because the test_assembler classes let us write Label references to
+// sections before the Labels' values are known, this gives us
+// flexibility in how we put the dump together: minidump pieces can
+// hold the file offsets of other minidump pieces before the
+// referents' positions have been decided. As long as everything has
+// been placed by the time we call dump.GetContents to obtain the
+// bytes, all the Labels' values will be known, and everything will
+// get patched up appropriately.
+//
+// The dump.Add(thing) functions append THINGS's contents to the
+// minidump, but they also do two other things:
+//
+// - dump.Add(thing) invokes thing->Finish, which tells *thing the
+// offset within the file at which it was placed, and allows *thing
+// to do any final content generation.
+//
+// - If THING is something which should receive an entry in some sort
+// of list or directory, then dump.Add(THING) automatically creates
+// the appropriate directory or list entry. Streams must appear in
+// the stream directory; memory ranges should be listed in the
+// memory list; threads should be placed in the thread list; and so
+// on.
+//
+// By convention, Section subclass constructors that take references
+// to other Sections do not take care of 'Add'ing their arguments to
+// the dump. For example, although the Thread constructor takes
+// references to a Memory and a Context, it does not add them to the
+// dump on the caller's behalf. Rather, the caller is responsible for
+// 'Add'ing every section they create. This allows Sections to be
+// cited from more than one place; for example, Memory ranges are
+// cited both from Thread objects (as their stack contents) and by the
+// memory list stream.
+//
+// If you forget to Add some Section, the Dump::GetContents call will
+// fail, as the test_assembler::Labels used to cite the Section's
+// contents from elsewhere will still be undefined.
+#ifndef PROCESSOR_SYNTH_MINIDUMP_H_
+#define PROCESSOR_SYNTH_MINIDUMP_H_
+
+#include <assert.h>
+
+#include <iostream>
+#include <string>
+
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+namespace SynthMinidump {
+
+using test_assembler::Endianness;
+using test_assembler::kBigEndian;
+using test_assembler::kLittleEndian;
+using test_assembler::kUnsetEndian;
+using test_assembler::Label;
+
+class Dump;
+class Memory;
+class String;
+
+// A test_assembler::Section which will be appended to a minidump.
+class Section: public test_assembler::Section {
+ public:
+ explicit Section(const Dump &dump);
+
+ // Append an MDLocationDescriptor referring to this section to SECTION.
+ // If 'this' is NULL, append a descriptor with a zero length and MDRVA.
+ //
+ // (I couldn't find the language in the C++ standard that says that
+ // invoking member functions of a NULL pointer to a class type is
+ // bad, if such language exists. Having this function handle NULL
+ // 'this' is convenient, but if it causes trouble, it's not hard to
+ // do differently.)
+ void CiteLocationIn(test_assembler::Section *section) const;
+
+ // Note that this section's contents are complete, and that it has
+ // been placed in the minidump file at OFFSET. The 'Add' member
+ // functions call the Finish member function of the object being
+ // added for you; if you are 'Add'ing this section, you needn't Finish it.
+ virtual void Finish(const Label &offset) {
+ file_offset_ = offset; size_ = Size();
+ }
+
+ protected:
+ // This section's size and offset within the minidump file.
+ Label file_offset_, size_;
+};
+
+// A stream within a minidump file. 'Add'ing a stream to a minidump
+// creates an entry for it in the minidump's stream directory.
+class Stream: public Section {
+ public:
+ // Create a stream of type TYPE. You can append whatever contents
+ // you like to this stream using the test_assembler::Section methods.
+ Stream(const Dump &dump, uint32_t type) : Section(dump), type_(type) { }
+
+ // Append an MDRawDirectory referring to this stream to SECTION.
+ void CiteStreamIn(test_assembler::Section *section) const;
+
+ private:
+ // The type of this stream.
+ uint32_t type_;
+};
+
+class SystemInfo: public Stream {
+ public:
+ // Create an MD_SYSTEM_INFO_STREAM stream belonging to DUMP holding
+ // an MDRawSystem info structure initialized with the values from
+ // SYSTEM_INFO, except that the csd_version field is replaced with
+ // the file offset of the string CSD_VERSION, which can be 'Add'ed
+ // to the dump at the desired location.
+ //
+ // Remember that you are still responsible for 'Add'ing CSD_VERSION
+ // to the dump yourself.
+ SystemInfo(const Dump &dump,
+ const MDRawSystemInfo &system_info,
+ const String &csd_version);
+
+ // Stock MDRawSystemInfo information and associated strings, for
+ // writing tests.
+ static const MDRawSystemInfo windows_x86;
+ static const string windows_x86_csd_version;
+};
+
+// An MDString: a string preceded by a 32-bit length.
+class String: public Section {
+ public:
+ String(const Dump &dump, const string &value);
+
+ // Append an MDRVA referring to this string to SECTION.
+ void CiteStringIn(test_assembler::Section *section) const;
+};
+
+// A range of memory contents. 'Add'ing a memory range to a minidump
+// creates n entry for it in the minidump's memory list. By
+// convention, the 'start', 'Here', and 'Mark' member functions refer
+// to memory addresses.
+class Memory: public Section {
+ public:
+ Memory(const Dump &dump, uint64_t address)
+ : Section(dump), address_(address) { start() = address; }
+
+ // Append an MDMemoryDescriptor referring to this memory range to SECTION.
+ void CiteMemoryIn(test_assembler::Section *section) const;
+
+ private:
+ // The process address from which these memory contents were taken.
+ // Shouldn't this be a Label?
+ uint64_t address_;
+};
+
+class Context: public Section {
+ public:
+ // Create a context belonging to DUMP whose contents are a copy of CONTEXT.
+ Context(const Dump &dump, const MDRawContextX86 &context);
+ Context(const Dump &dump, const MDRawContextARM &context);
+ Context(const Dump &dump, const MDRawContextMIPS &context);
+ // Add an empty context to the dump.
+ Context(const Dump &dump) : Section(dump) {}
+ // Add constructors for other architectures here. Remember to byteswap.
+};
+
+class Thread: public Section {
+ public:
+ // Create a thread belonging to DUMP with the given values, citing
+ // STACK and CONTEXT (which you must Add to the dump separately).
+ Thread(const Dump &dump,
+ uint32_t thread_id,
+ const Memory &stack,
+ const Context &context,
+ uint32_t suspend_count = 0,
+ uint32_t priority_class = 0,
+ uint32_t priority = 0,
+ uint64_t teb = 0);
+};
+
+class Module: public Section {
+ public:
+ // Create a module with the given values. Note that CV_RECORD and
+ // MISC_RECORD can be NULL, in which case the corresponding location
+ // descriptior in the minidump will have a length of zero.
+ Module(const Dump &dump,
+ uint64_t base_of_image,
+ uint32_t size_of_image,
+ const String &name,
+ uint32_t time_date_stamp = 1262805309,
+ uint32_t checksum = 0,
+ const MDVSFixedFileInfo &version_info = Module::stock_version_info,
+ const Section *cv_record = NULL,
+ const Section *misc_record = NULL);
+
+ private:
+ // A standard MDVSFixedFileInfo structure to use as a default for
+ // minidumps. There's no reason to make users write out all this crap
+ // over and over.
+ static const MDVSFixedFileInfo stock_version_info;
+};
+
+class Exception : public Stream {
+public:
+ Exception(const Dump &dump,
+ const Context &context,
+ uint32_t thread_id = 0,
+ uint32_t exception_code = 0,
+ uint32_t exception_flags = 0,
+ uint64_t exception_address = 0);
+};
+
+// A list of entries starting with a 32-bit count, like a memory list
+// or a thread list.
+template<typename Element>
+class List: public Stream {
+ public:
+ List(const Dump &dump, uint32_t type) : Stream(dump, type), count_(0) {
+ D32(count_label_);
+ }
+
+ // Add ELEMENT to this list.
+ void Add(Element *element) {
+ element->Finish(file_offset_ + Size());
+ Append(*element);
+ count_++;
+ }
+
+ // Return true if this List is empty, false otherwise.
+ bool Empty() { return count_ == 0; }
+
+ // Finish up the contents of this section, mark it as having been
+ // placed at OFFSET.
+ virtual void Finish(const Label &offset) {
+ Stream::Finish(offset);
+ count_label_ = count_;
+ }
+
+ private:
+ size_t count_;
+ Label count_label_;
+};
+
+class Dump: public test_assembler::Section {
+ public:
+
+ // Create a test_assembler::Section containing a minidump file whose
+ // header uses the given values. ENDIANNESS determines the
+ // endianness of the signature; we set this section's default
+ // endianness by this.
+ Dump(uint64_t flags,
+ Endianness endianness = kLittleEndian,
+ uint32_t version = MD_HEADER_VERSION,
+ uint32_t date_time_stamp = 1262805309);
+
+ // The following functions call OBJECT->Finish(), and append the
+ // contents of OBJECT to this minidump. They also record OBJECT in
+ // whatever directory or list is appropriate for its type. The
+ // stream directory, memory list, thread list, and module list are
+ // accumulated this way.
+ Dump &Add(SynthMinidump::Section *object); // simply append data
+ Dump &Add(Stream *object); // append, record in stream directory
+ Dump &Add(Memory *object); // append, record in memory list
+ Dump &Add(Thread *object); // append, record in thread list
+ Dump &Add(Module *object); // append, record in module list
+
+ // Complete the construction of the minidump, given the Add calls
+ // we've seen up to this point. After this call, this Dump's
+ // contents are complete, all labels should be defined if everything
+ // Cited has been Added, and you may call GetContents on it.
+ void Finish();
+
+ private:
+ // A label representing the start of the minidump file.
+ Label file_start_;
+
+ // The stream directory. We construct this incrementally from
+ // Add(Stream *) calls.
+ SynthMinidump::Section stream_directory_; // The directory's contents.
+ size_t stream_count_; // The number of streams so far.
+ Label stream_count_label_; // Cited in file header.
+ Label stream_directory_rva_; // The directory's file offset.
+
+ // This minidump's thread list. We construct this incrementally from
+ // Add(Thread *) calls.
+ List<Thread> thread_list_;
+
+ // This minidump's module list. We construct this incrementally from
+ // Add(Module *) calls.
+ List<Module> module_list_;
+
+ // This minidump's memory list. We construct this incrementally from
+ // Add(Memory *) calls. This is actually a list of MDMemoryDescriptors,
+ // not memory ranges --- thus the odd type.
+ List<SynthMinidump::Section> memory_list_;
+};
+
+} // namespace SynthMinidump
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_SYNTH_MINIDUMP_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc
new file mode 100644
index 000000000..8835b4493
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// synth_minidump_unittest.cc: Unit tests for google_breakpad::SynthMinidump
+// classes.
+
+#include <sstream>
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "processor/synth_minidump.h"
+#include "processor/synth_minidump_unittest_data.h"
+
+using google_breakpad::SynthMinidump::Context;
+using google_breakpad::SynthMinidump::Dump;
+using google_breakpad::SynthMinidump::Exception;
+using google_breakpad::SynthMinidump::List;
+using google_breakpad::SynthMinidump::Memory;
+using google_breakpad::SynthMinidump::Module;
+using google_breakpad::SynthMinidump::Section;
+using google_breakpad::SynthMinidump::Stream;
+using google_breakpad::SynthMinidump::String;
+using google_breakpad::SynthMinidump::SystemInfo;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Label;
+
+TEST(Section, Simple) {
+ Dump dump(0);
+ Section section(dump);
+ section.L32(0x12345678);
+ section.Finish(0);
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x78\x56\x34\x12", 4), contents);
+}
+
+TEST(Section, CiteLocationIn) {
+ Dump dump(0, kBigEndian);
+ Section section1(dump), section2(dump);
+ section1.Append("order");
+ section2.Append("mayhem");
+ section2.Finish(0x32287ec2);
+ section2.CiteLocationIn(&section1);
+ string contents;
+ ASSERT_TRUE(section1.GetContents(&contents));
+ string expected("order\0\0\0\x06\x32\x28\x7e\xc2", 13);
+ EXPECT_EQ(expected, contents);
+}
+
+TEST(Stream, CiteStreamIn) {
+ Dump dump(0, kLittleEndian);
+ Stream stream(dump, 0x40cae2b3);
+ Section section(dump);
+ stream.Append("stream contents");
+ section.Append("section contents");
+ stream.Finish(0x41424344);
+ stream.CiteStreamIn(&section);
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+ string expected("section contents"
+ "\xb3\xe2\xca\x40"
+ "\x0f\0\0\0"
+ "\x44\x43\x42\x41",
+ 16 + 4 + 4 + 4);
+ EXPECT_EQ(expected, contents);
+}
+
+TEST(Memory, CiteMemoryIn) {
+ Dump dump(0, kBigEndian);
+ Memory memory(dump, 0x76d010874ab019f9ULL);
+ Section section(dump);
+ memory.Append("memory contents");
+ section.Append("section contents");
+ memory.Finish(0x51525354);
+ memory.CiteMemoryIn(&section);
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+ string expected("section contents"
+ "\x76\xd0\x10\x87\x4a\xb0\x19\xf9"
+ "\0\0\0\x0f"
+ "\x51\x52\x53\x54",
+ 16 + 8 + 4 + 4);
+ EXPECT_EQ(contents, expected);
+}
+
+TEST(Memory, Here) {
+ Dump dump(0, kBigEndian);
+ Memory memory(dump, 0x89979731eb060ed4ULL);
+ memory.Append(1729, 42);
+ Label l = memory.Here();
+ ASSERT_EQ(0x89979731eb060ed4ULL + 1729, l.Value());
+}
+
+TEST(Context, X86) {
+ Dump dump(0, kLittleEndian);
+ assert(x86_raw_context.context_flags & MD_CONTEXT_X86);
+ Context context(dump, x86_raw_context);
+ string contents;
+ ASSERT_TRUE(context.GetContents(&contents));
+ EXPECT_EQ(sizeof(x86_expected_contents), contents.size());
+ EXPECT_TRUE(memcmp(contents.data(), x86_expected_contents, contents.size())
+ == 0);
+}
+
+TEST(Context, ARM) {
+ Dump dump(0, kLittleEndian);
+ assert(arm_raw_context.context_flags & MD_CONTEXT_ARM);
+ Context context(dump, arm_raw_context);
+ string contents;
+ ASSERT_TRUE(context.GetContents(&contents));
+ EXPECT_EQ(sizeof(arm_expected_contents), contents.size());
+ EXPECT_TRUE(memcmp(contents.data(), arm_expected_contents, contents.size())
+ == 0);
+}
+
+TEST(ContextDeathTest, X86BadFlags) {
+ Dump dump(0, kLittleEndian);
+ MDRawContextX86 raw;
+ raw.context_flags = MD_CONTEXT_AMD64;
+ ASSERT_DEATH(Context context(dump, raw);,
+ "context\\.context_flags & (0x[0-9a-f]+|MD_CONTEXT_X86)");
+}
+
+TEST(ContextDeathTest, X86BadEndianness) {
+ Dump dump(0, kBigEndian);
+ MDRawContextX86 raw;
+ raw.context_flags = MD_CONTEXT_X86;
+ ASSERT_DEATH(Context context(dump, raw);,
+ "dump\\.endianness\\(\\) == kLittleEndian");
+}
+
+TEST(Thread, Simple) {
+ Dump dump(0, kLittleEndian);
+ Context context(dump, x86_raw_context);
+ context.Finish(0x8665da0c);
+ Memory stack(dump, 0xaad55a93cc3c0efcULL);
+ stack.Append("stack contents");
+ stack.Finish(0xe08cdbd1);
+ google_breakpad::SynthMinidump::Thread thread(
+ dump, 0x3d7ec360, stack, context,
+ 0x3593f44d, // suspend count
+ 0xab352b82, // priority class
+ 0x2753d838, // priority
+ 0xeb2de4be3f29e3e9ULL); // thread environment block
+ string contents;
+ ASSERT_TRUE(thread.GetContents(&contents));
+ static const uint8_t expected_bytes[] = {
+ 0x60, 0xc3, 0x7e, 0x3d, // thread id
+ 0x4d, 0xf4, 0x93, 0x35, // suspend count
+ 0x82, 0x2b, 0x35, 0xab, // priority class
+ 0x38, 0xd8, 0x53, 0x27, // priority
+ 0xe9, 0xe3, 0x29, 0x3f, 0xbe, 0xe4, 0x2d, 0xeb, // thread environment block
+ 0xfc, 0x0e, 0x3c, 0xcc, 0x93, 0x5a, 0xd5, 0xaa, // stack address
+ 0x0e, 0x00, 0x00, 0x00, // stack size
+ 0xd1, 0xdb, 0x8c, 0xe0, // stack MDRVA
+ 0xcc, 0x02, 0x00, 0x00, // context size
+ 0x0c, 0xda, 0x65, 0x86 // context MDRVA
+ };
+ EXPECT_EQ(sizeof(expected_bytes), contents.size());
+ EXPECT_TRUE(memcmp(contents.data(), expected_bytes, contents.size()) == 0);
+}
+
+TEST(Exception, Simple) {
+ Dump dump(0, kLittleEndian);
+ Context context(dump, x86_raw_context);
+ context.Finish(0x8665da0c);
+
+ Exception exception(dump, context,
+ 0x1234abcd, // thread id
+ 0xdcba4321, // exception code
+ 0xf0e0d0c0, // exception flags
+ 0x0919a9b9c9d9e9f9ULL); // exception address
+ string contents;
+ ASSERT_TRUE(exception.GetContents(&contents));
+ static const uint8_t expected_bytes[] = {
+ 0xcd, 0xab, 0x34, 0x12, // thread id
+ 0x00, 0x00, 0x00, 0x00, // __align
+ 0x21, 0x43, 0xba, 0xdc, // exception code
+ 0xc0, 0xd0, 0xe0, 0xf0, // exception flags
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception record
+ 0xf9, 0xe9, 0xd9, 0xc9, 0xb9, 0xa9, 0x19, 0x09, // exception address
+ 0x00, 0x00, 0x00, 0x00, // number parameters
+ 0x00, 0x00, 0x00, 0x00, // __align
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information
+ 0xcc, 0x02, 0x00, 0x00, // context size
+ 0x0c, 0xda, 0x65, 0x86 // context MDRVA
+ };
+ EXPECT_EQ(sizeof(expected_bytes), contents.size());
+ EXPECT_TRUE(memcmp(contents.data(), expected_bytes, contents.size()) == 0);
+}
+
+TEST(String, Simple) {
+ Dump dump(0, kBigEndian);
+ String s(dump, "All mimsy were the borogoves");
+ string contents;
+ ASSERT_TRUE(s.GetContents(&contents));
+ static const char expected[] =
+ "\x00\x00\x00\x38\0A\0l\0l\0 \0m\0i\0m\0s\0y\0 \0w\0e\0r\0e"
+ "\0 \0t\0h\0e\0 \0b\0o\0r\0o\0g\0o\0v\0e\0s";
+ string expected_string(expected, sizeof(expected) - 1);
+ EXPECT_EQ(expected_string, contents);
+}
+
+TEST(String, CiteStringIn) {
+ Dump dump(0, kLittleEndian);
+ String s(dump, "and the mome wraths outgrabe");
+ Section section(dump);
+ section.Append("initial");
+ s.CiteStringIn(&section);
+ s.Finish(0xdc2bb469);
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("initial\x69\xb4\x2b\xdc", 7 + 4), contents);
+}
+
+TEST(List, Empty) {
+ Dump dump(0, kBigEndian);
+ List<Section> list(dump, 0x2442779c);
+ EXPECT_TRUE(list.Empty());
+ list.Finish(0x84e09808);
+ string contents;
+ ASSERT_TRUE(list.GetContents(&contents));
+ EXPECT_EQ(string("\0\0\0\0", 4), contents);
+}
+
+TEST(List, Two) {
+ Dump dump(0, kBigEndian);
+ List<Section> list(dump, 0x26c9f498);
+ Section section1(dump);
+ section1.Append("section one contents");
+ EXPECT_TRUE(list.Empty());
+ list.Add(&section1);
+ EXPECT_FALSE(list.Empty());
+ Section section2(dump);
+ section2.Append("section two contents");
+ list.Add(&section2);
+ list.Finish(0x1e5bb60e);
+ string contents;
+ ASSERT_TRUE(list.GetContents(&contents));
+ EXPECT_EQ(string("\0\0\0\x02section one contentssection two contents", 44),
+ contents);
+}
+
+TEST(Dump, Header) {
+ Dump dump(0x9f738b33685cc84cULL, kLittleEndian, 0xb3817faf, 0x2c741c0a);
+ dump.Finish();
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ ASSERT_EQ(string("\x4d\x44\x4d\x50" // signature
+ "\xaf\x7f\x81\xb3" // version
+ "\0\0\0\0" // stream count
+ "\x20\0\0\0" // directory RVA (could be anything)
+ "\0\0\0\0" // checksum
+ "\x0a\x1c\x74\x2c" // time_date_stamp
+ "\x4c\xc8\x5c\x68\x33\x8b\x73\x9f", // flags
+ 32),
+ contents);
+}
+
+TEST(Dump, HeaderBigEndian) {
+ Dump dump(0x206ce3cc6fb8e0f0ULL, kBigEndian, 0x161693e2, 0x35667744);
+ dump.Finish();
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ ASSERT_EQ(string("\x50\x4d\x44\x4d" // signature
+ "\x16\x16\x93\xe2" // version
+ "\0\0\0\0" // stream count
+ "\0\0\0\x20" // directory RVA (could be anything)
+ "\0\0\0\0" // checksum
+ "\x35\x66\x77\x44" // time_date_stamp
+ "\x20\x6c\xe3\xcc\x6f\xb8\xe0\xf0", // flags
+ 32),
+ contents);
+}
+
+TEST(Dump, OneSection) {
+ Dump dump(0, kLittleEndian);
+ Section section(dump);
+ section.Append("section contents");
+ dump.Add(&section);
+ dump.Finish();
+ string dump_contents;
+ // Just check for undefined labels; don't worry about the contents.
+ ASSERT_TRUE(dump.GetContents(&dump_contents));
+
+ Section referencing_section(dump);
+ section.CiteLocationIn(&referencing_section);
+ string contents;
+ ASSERT_TRUE(referencing_section.GetContents(&contents));
+ ASSERT_EQ(string("\x10\0\0\0\x20\0\0\0", 8), contents);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h
new file mode 100644
index 000000000..3403372e6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h
@@ -0,0 +1,418 @@
+// -*- mode: C++ -*-
+
+// Not copyrightable: random test data.
+// synth_minidump_unittest_data.h: verbose test data for SynthMinidump tests.
+
+#ifndef PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_
+#define PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_
+
+#include "google_breakpad/common/minidump_format.h"
+
+static const MDRawContextX86 x86_raw_context = {
+ 0xded5d71b, // context_flags
+ 0x9fdb432e, // dr0
+ 0x26b7a81a, // dr1
+ 0xcac7e348, // dr2
+ 0xcf99ec09, // dr3
+ 0x7dc8c2cd, // dr6
+ 0x21deb880, // dr7
+
+ // float_save
+ {
+ 0x8a5d2bb0, // control_word
+ 0x0286c4c9, // status_word
+ 0xf1feea21, // tag_word
+ 0xb2d40576, // error_offset
+ 0x48146cde, // error_selector
+ 0x983f9b21, // data_offset
+ 0x475be12c, // data_selector
+
+ // register_area
+ {
+ 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5,
+ 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe,
+ 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33,
+ 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae,
+ 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9,
+ 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67,
+ 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04,
+ 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69,
+ 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7,
+ 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d
+ },
+
+ 0x84c53a90, // cr0_npx_state
+ },
+
+ 0x79f71e76, // gs
+ 0x8107bd25, // fs
+ 0x452d2921, // es
+ 0x87ec2875, // ds
+ 0xf8bb73f5, // edi
+ 0xa63ebb88, // esi
+ 0x95d35ebe, // ebx
+ 0x17aa2456, // edx
+ 0x135fa208, // ecx
+ 0x500615e6, // eax
+ 0x66d14205, // ebp
+ 0x000719a5, // eip
+ 0x477b481b, // cs
+ 0x8684dfba, // eflags
+ 0xe33ccddf, // esp
+ 0xc0e65d33, // ss
+
+ // extended_registers
+ {
+ 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5,
+ 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3,
+ 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41,
+ 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0,
+ 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28,
+ 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd,
+ 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52,
+ 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44,
+ 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9,
+ 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7,
+ 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14,
+ 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53,
+ 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe,
+ 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9,
+ 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97,
+ 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9,
+ 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c,
+ 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7,
+ 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83,
+ 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d,
+ 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4,
+ 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c,
+ 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f,
+ 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0,
+ 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde,
+ 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1,
+ 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49,
+ 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96,
+ 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a,
+ 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51,
+ 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6,
+ 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2,
+ 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24,
+ 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3,
+ 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2,
+ 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e,
+ 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9,
+ 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68,
+ 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6,
+ 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda,
+ 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14,
+ 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed,
+ 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4,
+ 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff,
+ 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f,
+ 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58,
+ 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd,
+ 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4,
+ 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff,
+ 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90,
+ 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36,
+ 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0,
+ 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2,
+ 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60,
+ 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf,
+ 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa,
+ 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e,
+ 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb,
+ 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c,
+ 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1,
+ 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7,
+ 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8,
+ 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26,
+ 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba
+ }
+};
+
+static const uint8_t x86_expected_contents[] = {
+ 0x1b, 0xd7, 0xd5, 0xde,
+ 0x2e, 0x43, 0xdb, 0x9f,
+ 0x1a, 0xa8, 0xb7, 0x26,
+ 0x48, 0xe3, 0xc7, 0xca,
+ 0x09, 0xec, 0x99, 0xcf,
+ 0xcd, 0xc2, 0xc8, 0x7d,
+ 0x80, 0xb8, 0xde, 0x21,
+ 0xb0, 0x2b, 0x5d, 0x8a,
+ 0xc9, 0xc4, 0x86, 0x02,
+ 0x21, 0xea, 0xfe, 0xf1,
+ 0x76, 0x05, 0xd4, 0xb2,
+ 0xde, 0x6c, 0x14, 0x48,
+ 0x21, 0x9b, 0x3f, 0x98,
+ 0x2c, 0xe1, 0x5b, 0x47,
+
+ // float_save.register_area --- unswapped
+ 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5,
+ 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe,
+ 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33,
+ 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae,
+ 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9,
+ 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67,
+ 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04,
+ 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69,
+ 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7,
+ 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d,
+
+ 0x90, 0x3a, 0xc5, 0x84,
+ 0x76, 0x1e, 0xf7, 0x79,
+ 0x25, 0xbd, 0x07, 0x81,
+ 0x21, 0x29, 0x2d, 0x45,
+ 0x75, 0x28, 0xec, 0x87,
+ 0xf5, 0x73, 0xbb, 0xf8,
+ 0x88, 0xbb, 0x3e, 0xa6,
+ 0xbe, 0x5e, 0xd3, 0x95,
+ 0x56, 0x24, 0xaa, 0x17,
+ 0x08, 0xa2, 0x5f, 0x13,
+ 0xe6, 0x15, 0x06, 0x50,
+ 0x05, 0x42, 0xd1, 0x66,
+ 0xa5, 0x19, 0x07, 0x00,
+ 0x1b, 0x48, 0x7b, 0x47,
+ 0xba, 0xdf, 0x84, 0x86,
+ 0xdf, 0xcd, 0x3c, 0xe3,
+ 0x33, 0x5d, 0xe6, 0xc0,
+
+ // extended_registers --- unswapped
+ 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5,
+ 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3,
+ 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41,
+ 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0,
+ 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28,
+ 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd,
+ 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52,
+ 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44,
+ 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9,
+ 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7,
+ 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14,
+ 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53,
+ 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe,
+ 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9,
+ 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97,
+ 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9,
+ 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c,
+ 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7,
+ 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83,
+ 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d,
+ 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4,
+ 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c,
+ 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f,
+ 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0,
+ 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde,
+ 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1,
+ 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49,
+ 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96,
+ 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a,
+ 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51,
+ 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6,
+ 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2,
+ 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24,
+ 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3,
+ 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2,
+ 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e,
+ 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9,
+ 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68,
+ 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6,
+ 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda,
+ 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14,
+ 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed,
+ 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4,
+ 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff,
+ 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f,
+ 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58,
+ 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd,
+ 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4,
+ 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff,
+ 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90,
+ 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36,
+ 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0,
+ 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2,
+ 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60,
+ 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf,
+ 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa,
+ 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e,
+ 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb,
+ 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c,
+ 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1,
+ 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7,
+ 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8,
+ 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26,
+ 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba
+};
+
+static const MDRawContextARM arm_raw_context = {
+ // context_flags
+ 0x591b9e6a,
+ // iregs
+ {
+ 0xa21594de,
+ 0x820d8a25,
+ 0xc4e133b2,
+ 0x173a1c02,
+ 0x105fb175,
+ 0xe871793f,
+ 0x5def70b3,
+ 0xcee3a623,
+ 0x7b3aa9b8,
+ 0x52518537,
+ 0x627012c5,
+ 0x22723dcc,
+ 0x16fcc971,
+ 0x20988bcb,
+ 0xf1ab806b,
+ 0x99d5fc03,
+ },
+ // cpsr
+ 0xb70df511,
+ // float_save
+ {
+ // fpscr
+ 0xa1e1f7ce1077e6b5ULL,
+ // regs
+ {
+ 0xbcb8d002eed7fbdeULL,
+ 0x4dd26a43b96ae97fULL,
+ 0x8eec22db8b31741cULL,
+ 0xfd634bd7c5ad66a0ULL,
+ 0x1681da0daeb3debeULL,
+ 0x474a32bdf72d0b71ULL,
+ 0xcaf464f8b1044834ULL,
+ 0xcaa6592ae5c7582aULL,
+ 0x4ee46889d877c3dbULL,
+ 0xf8930cf301645cf5ULL,
+ 0x4da7e9ebba27f7c7ULL,
+ 0x69a7b02761944da3ULL,
+ 0x2cda2b2e78195c06ULL,
+ 0x66b227ab9b460a42ULL,
+ 0x7e77e49e52ee0849ULL,
+ 0xd62cd9663e76f255ULL,
+ 0xe9370f082451514bULL,
+ 0x50a1c674dd1b6029ULL,
+ 0x405db4575829eac4ULL,
+ 0x67b948764649eee7ULL,
+ 0x93731885419229d4ULL,
+ 0xdb0338bad72a4ce7ULL,
+ 0xa0a451f996fca4c8ULL,
+ 0xb4508ea668400a45ULL,
+ 0xbff28c5c7a142423ULL,
+ 0x4f31b42b96f3a431ULL,
+ 0x2ce6789d4ea1ff37ULL,
+ 0xfa150b52e4f82a3cULL,
+ 0xe9ec40449e6ed4f3ULL,
+ 0x5ceca87836fe2251ULL,
+ 0x66f50de463ee238cULL,
+ 0x42823efcd59ab511ULL,
+ },
+ // extra
+ {
+ 0xe9e14cd2,
+ 0x865bb640,
+ 0x9f3f0b3e,
+ 0x94a71c52,
+ 0x3c012f19,
+ 0x6436637c,
+ 0x46ccedcb,
+ 0x7b341be7,
+ }
+ }
+};
+
+static const uint8_t arm_expected_contents[] = {
+ 0x6a, 0x9e, 0x1b, 0x59,
+ 0xde, 0x94, 0x15, 0xa2,
+ 0x25, 0x8a, 0x0d, 0x82,
+ 0xb2, 0x33, 0xe1, 0xc4,
+ 0x02, 0x1c, 0x3a, 0x17,
+ 0x75, 0xb1, 0x5f, 0x10,
+ 0x3f, 0x79, 0x71, 0xe8,
+ 0xb3, 0x70, 0xef, 0x5d,
+ 0x23, 0xa6, 0xe3, 0xce,
+ 0xb8, 0xa9, 0x3a, 0x7b,
+ 0x37, 0x85, 0x51, 0x52,
+ 0xc5, 0x12, 0x70, 0x62,
+ 0xcc, 0x3d, 0x72, 0x22,
+ 0x71, 0xc9, 0xfc, 0x16,
+ 0xcb, 0x8b, 0x98, 0x20,
+ 0x6b, 0x80, 0xab, 0xf1,
+ 0x03, 0xfc, 0xd5, 0x99,
+ 0x11, 0xf5, 0x0d, 0xb7,
+ 0xb5, 0xe6, 0x77, 0x10,
+ 0xce, 0xf7, 0xe1, 0xa1,
+ 0xde, 0xfb, 0xd7, 0xee,
+ 0x02, 0xd0, 0xb8, 0xbc,
+ 0x7f, 0xe9, 0x6a, 0xb9,
+ 0x43, 0x6a, 0xd2, 0x4d,
+ 0x1c, 0x74, 0x31, 0x8b,
+ 0xdb, 0x22, 0xec, 0x8e,
+ 0xa0, 0x66, 0xad, 0xc5,
+ 0xd7, 0x4b, 0x63, 0xfd,
+ 0xbe, 0xde, 0xb3, 0xae,
+ 0x0d, 0xda, 0x81, 0x16,
+ 0x71, 0x0b, 0x2d, 0xf7,
+ 0xbd, 0x32, 0x4a, 0x47,
+ 0x34, 0x48, 0x04, 0xb1,
+ 0xf8, 0x64, 0xf4, 0xca,
+ 0x2a, 0x58, 0xc7, 0xe5,
+ 0x2a, 0x59, 0xa6, 0xca,
+ 0xdb, 0xc3, 0x77, 0xd8,
+ 0x89, 0x68, 0xe4, 0x4e,
+ 0xf5, 0x5c, 0x64, 0x01,
+ 0xf3, 0x0c, 0x93, 0xf8,
+ 0xc7, 0xf7, 0x27, 0xba,
+ 0xeb, 0xe9, 0xa7, 0x4d,
+ 0xa3, 0x4d, 0x94, 0x61,
+ 0x27, 0xb0, 0xa7, 0x69,
+ 0x06, 0x5c, 0x19, 0x78,
+ 0x2e, 0x2b, 0xda, 0x2c,
+ 0x42, 0x0a, 0x46, 0x9b,
+ 0xab, 0x27, 0xb2, 0x66,
+ 0x49, 0x08, 0xee, 0x52,
+ 0x9e, 0xe4, 0x77, 0x7e,
+ 0x55, 0xf2, 0x76, 0x3e,
+ 0x66, 0xd9, 0x2c, 0xd6,
+ 0x4b, 0x51, 0x51, 0x24,
+ 0x08, 0x0f, 0x37, 0xe9,
+ 0x29, 0x60, 0x1b, 0xdd,
+ 0x74, 0xc6, 0xa1, 0x50,
+ 0xc4, 0xea, 0x29, 0x58,
+ 0x57, 0xb4, 0x5d, 0x40,
+ 0xe7, 0xee, 0x49, 0x46,
+ 0x76, 0x48, 0xb9, 0x67,
+ 0xd4, 0x29, 0x92, 0x41,
+ 0x85, 0x18, 0x73, 0x93,
+ 0xe7, 0x4c, 0x2a, 0xd7,
+ 0xba, 0x38, 0x03, 0xdb,
+ 0xc8, 0xa4, 0xfc, 0x96,
+ 0xf9, 0x51, 0xa4, 0xa0,
+ 0x45, 0x0a, 0x40, 0x68,
+ 0xa6, 0x8e, 0x50, 0xb4,
+ 0x23, 0x24, 0x14, 0x7a,
+ 0x5c, 0x8c, 0xf2, 0xbf,
+ 0x31, 0xa4, 0xf3, 0x96,
+ 0x2b, 0xb4, 0x31, 0x4f,
+ 0x37, 0xff, 0xa1, 0x4e,
+ 0x9d, 0x78, 0xe6, 0x2c,
+ 0x3c, 0x2a, 0xf8, 0xe4,
+ 0x52, 0x0b, 0x15, 0xfa,
+ 0xf3, 0xd4, 0x6e, 0x9e,
+ 0x44, 0x40, 0xec, 0xe9,
+ 0x51, 0x22, 0xfe, 0x36,
+ 0x78, 0xa8, 0xec, 0x5c,
+ 0x8c, 0x23, 0xee, 0x63,
+ 0xe4, 0x0d, 0xf5, 0x66,
+ 0x11, 0xb5, 0x9a, 0xd5,
+ 0xfc, 0x3e, 0x82, 0x42,
+ 0xd2, 0x4c, 0xe1, 0xe9,
+ 0x40, 0xb6, 0x5b, 0x86,
+ 0x3e, 0x0b, 0x3f, 0x9f,
+ 0x52, 0x1c, 0xa7, 0x94,
+ 0x19, 0x2f, 0x01, 0x3c,
+ 0x7c, 0x63, 0x36, 0x64,
+ 0xcb, 0xed, 0xcc, 0x46,
+ 0xe7, 0x1b, 0x34, 0x7b
+};
+
+#endif // PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/tokenize.cc b/toolkit/crashreporter/google-breakpad/src/processor/tokenize.cc
new file mode 100644
index 000000000..8fce87a22
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/tokenize.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+#ifdef _MSC_VER
+#define strtok_r strtok_s
+#endif
+
+using std::vector;
+
+bool Tokenize(char *line,
+ const char *separators,
+ int max_tokens,
+ vector<char*> *tokens) {
+ tokens->clear();
+ tokens->reserve(max_tokens);
+
+ int remaining = max_tokens;
+
+ // Split tokens on the separator character.
+ // strip them out before exhausting max_tokens.
+ char *save_ptr;
+ char *token = strtok_r(line, separators, &save_ptr);
+ while (token && --remaining > 0) {
+ tokens->push_back(token);
+ if (remaining > 1)
+ token = strtok_r(NULL, separators, &save_ptr);
+ }
+
+ // If there's anything left, just add it as a single token.
+ if (remaining == 0 && (token = strtok_r(NULL, "\r\n", &save_ptr))) {
+ tokens->push_back(token);
+ }
+
+ return tokens->size() == static_cast<unsigned int>(max_tokens);
+}
+
+void StringToVector(const string &str, vector<char> &vec) {
+ vec.resize(str.length() + 1);
+ std::copy(str.begin(), str.end(),
+ vec.begin());
+ vec[str.length()] = '\0';
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/tokenize.h b/toolkit/crashreporter/google-breakpad/src/processor/tokenize.h
new file mode 100644
index 000000000..9ff571d5c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/tokenize.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Implements a Tokenize function for splitting up strings.
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_
+#define GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_
+
+#include <string>
+#include <vector>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+// Splits line into at most max_tokens tokens, separated by any of the
+// characters in separators and placing them in the tokens vector.
+// line is a 0-terminated string that optionally ends with a newline
+// character or combination, which will be removed.
+// If more tokens than max_tokens are present, the final token is placed
+// into the vector without splitting it up at all. This modifies line as
+// a side effect. Returns true if exactly max_tokens tokens are returned,
+// and false if fewer are returned. This is not considered a failure of
+// Tokenize, but may be treated as a failure if the caller expects an
+// exact, as opposed to maximum, number of tokens.
+
+bool Tokenize(char *line,
+ const char *separators,
+ int max_tokens,
+ std::vector<char*> *tokens);
+// For convenience, since you need a char* to pass to Tokenize.
+// You can call StringToVector on a string, and use &vec[0].
+void StringToVector(const string &str, std::vector<char> &vec);
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h b/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h
new file mode 100644
index 000000000..f96e0a438
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// windows_frame_info.h: Holds debugging information about a stack frame.
+//
+// This structure is specific to Windows debugging information obtained
+// from pdb files using the DIA API.
+//
+// Author: Mark Mentovai
+
+
+#ifndef PROCESSOR_WINDOWS_FRAME_INFO_H__
+#define PROCESSOR_WINDOWS_FRAME_INFO_H__
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "processor/logging.h"
+#include "processor/tokenize.h"
+
+namespace google_breakpad {
+
+#ifdef _WIN32
+#define strtoull _strtoui64
+#endif
+
+struct WindowsFrameInfo {
+ public:
+ enum Validity {
+ VALID_NONE = 0,
+ VALID_PARAMETER_SIZE = 1,
+ VALID_ALL = -1
+ };
+
+ // The types for stack_info_. This is equivalent to MS DIA's
+ // StackFrameTypeEnum. Each identifies a different type of frame
+ // information, although all are represented in the symbol file in the
+ // same format. These are used as indices to the stack_info_ array.
+ enum StackInfoTypes {
+ STACK_INFO_FPO = 0,
+ STACK_INFO_TRAP, // not used here
+ STACK_INFO_TSS, // not used here
+ STACK_INFO_STANDARD,
+ STACK_INFO_FRAME_DATA,
+ STACK_INFO_LAST, // must be the last sequentially-numbered item
+ STACK_INFO_UNKNOWN = -1
+ };
+
+ WindowsFrameInfo() : type_(STACK_INFO_UNKNOWN),
+ valid(VALID_NONE),
+ prolog_size(0),
+ epilog_size(0),
+ parameter_size(0),
+ saved_register_size(0),
+ local_size(0),
+ max_stack_size(0),
+ allocates_base_pointer(0),
+ program_string() {}
+
+ WindowsFrameInfo(StackInfoTypes type,
+ uint32_t set_prolog_size,
+ uint32_t set_epilog_size,
+ uint32_t set_parameter_size,
+ uint32_t set_saved_register_size,
+ uint32_t set_local_size,
+ uint32_t set_max_stack_size,
+ int set_allocates_base_pointer,
+ const string set_program_string)
+ : type_(type),
+ valid(VALID_ALL),
+ prolog_size(set_prolog_size),
+ epilog_size(set_epilog_size),
+ parameter_size(set_parameter_size),
+ saved_register_size(set_saved_register_size),
+ local_size(set_local_size),
+ max_stack_size(set_max_stack_size),
+ allocates_base_pointer(set_allocates_base_pointer),
+ program_string(set_program_string) {}
+
+ // Parse a textual serialization of a WindowsFrameInfo object from
+ // a string. Returns NULL if parsing fails, or a new object
+ // otherwise. type, rva and code_size are present in the STACK line,
+ // but not the StackFrameInfo structure, so return them as outparams.
+ static WindowsFrameInfo *ParseFromString(const string string,
+ int &type,
+ uint64_t &rva,
+ uint64_t &code_size) {
+ // The format of a STACK WIN record is documented at:
+ //
+ // https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+
+ std::vector<char> buffer;
+ StringToVector(string, buffer);
+ std::vector<char*> tokens;
+ if (!Tokenize(&buffer[0], " \r\n", 11, &tokens))
+ return NULL;
+
+ type = strtol(tokens[0], NULL, 16);
+ if (type < 0 || type > STACK_INFO_LAST - 1)
+ return NULL;
+
+ rva = strtoull(tokens[1], NULL, 16);
+ code_size = strtoull(tokens[2], NULL, 16);
+ uint32_t prolog_size = strtoul(tokens[3], NULL, 16);
+ uint32_t epilog_size = strtoul(tokens[4], NULL, 16);
+ uint32_t parameter_size = strtoul(tokens[5], NULL, 16);
+ uint32_t saved_register_size = strtoul(tokens[6], NULL, 16);
+ uint32_t local_size = strtoul(tokens[7], NULL, 16);
+ uint32_t max_stack_size = strtoul(tokens[8], NULL, 16);
+ int has_program_string = strtoul(tokens[9], NULL, 16);
+
+ const char *program_string = "";
+ int allocates_base_pointer = 0;
+ if (has_program_string) {
+ program_string = tokens[10];
+ } else {
+ allocates_base_pointer = strtoul(tokens[10], NULL, 16);
+ }
+
+ return new WindowsFrameInfo(static_cast<StackInfoTypes>(type),
+ prolog_size,
+ epilog_size,
+ parameter_size,
+ saved_register_size,
+ local_size,
+ max_stack_size,
+ allocates_base_pointer,
+ program_string);
+ }
+
+ // CopyFrom makes "this" WindowsFrameInfo object identical to "that".
+ void CopyFrom(const WindowsFrameInfo &that) {
+ type_ = that.type_;
+ valid = that.valid;
+ prolog_size = that.prolog_size;
+ epilog_size = that.epilog_size;
+ parameter_size = that.parameter_size;
+ saved_register_size = that.saved_register_size;
+ local_size = that.local_size;
+ max_stack_size = that.max_stack_size;
+ allocates_base_pointer = that.allocates_base_pointer;
+ program_string = that.program_string;
+ }
+
+ // Clears the WindowsFrameInfo object so that users will see it as though
+ // it contains no information.
+ void Clear() {
+ type_ = STACK_INFO_UNKNOWN;
+ valid = VALID_NONE;
+ program_string.erase();
+ }
+
+ StackInfoTypes type_;
+
+ // Identifies which fields in the structure are valid. This is of
+ // type Validity, but it is defined as an int because it's not
+ // possible to OR values into an enumerated type. Users must check
+ // this field before using any other.
+ int valid;
+
+ // These values come from IDiaFrameData.
+ uint32_t prolog_size;
+ uint32_t epilog_size;
+ uint32_t parameter_size;
+ uint32_t saved_register_size;
+ uint32_t local_size;
+ uint32_t max_stack_size;
+
+ // Only one of allocates_base_pointer or program_string will be valid.
+ // If program_string is empty, use allocates_base_pointer.
+ bool allocates_base_pointer;
+ string program_string;
+};
+
+} // namespace google_breakpad
+
+
+#endif // PROCESSOR_WINDOWS_FRAME_INFO_H__