summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/breakpad-client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/crashreporter/breakpad-client
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/breakpad-client')
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/crash_generation/client_info.h68
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.cc105
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.h65
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.cc383
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.h135
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/data/linux-gate-amd.sym3
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/data/linux-gate-intel.sym3
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/mapping_info.h75
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/raw_context_cpu.h53
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.cc305
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.h91
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.cc259
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.h65
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.cc876
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.h289
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/exception_handler_unittest.cc1290
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.cc108
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.h48
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/microdump_extra_info.h52
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.cc96
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.h199
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/log/log.cc84
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/log/log.h55
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc664
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.h68
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer_unittest.cc421
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set.h144
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set_unittest.cc164
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader.h106
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader_unittest.cc78
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader.h131
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader_unittest.cc169
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.cc308
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.h130
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper_unittest.cc192
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc999
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h333
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper_unittest_helper.cc95
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc403
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.h106
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc580
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc1562
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.h143
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest.cc934
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.cc66
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.h49
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader.h130
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc199
-rw-r--r--toolkit/crashreporter/breakpad-client/linux/moz.build47
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/client_info.h49
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.cc74
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.h66
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.cc164
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.h151
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/crash_generation/moz.build18
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.cc402
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.h48
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc625
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h386
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc991
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h297
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/mach_vm_compat.h88
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc2135
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h290
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/minidump_test.xcodeproj/project.pbxproj843
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/moz.build22
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.cc92
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.h85
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.cc79
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.h52
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.cc106
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.h62
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.h46
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.mm60
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_dwarf_databin0 -> 702795 bytes
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym5300
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/handler/ucontext_compat.h47
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/BreakpadFramework_Test.mm217
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/crash_generation_server_test.cc398
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/exception_handler_test.cc714
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test.cc320
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test_helper.cc74
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/spawn_child_process.h149
-rw-r--r--toolkit/crashreporter/breakpad-client/mac/tests/testlogging.h9
-rw-r--r--toolkit/crashreporter/breakpad-client/minidump_file_writer-inl.h97
-rw-r--r--toolkit/crashreporter/breakpad-client/minidump_file_writer.cc402
-rw-r--r--toolkit/crashreporter/breakpad-client/minidump_file_writer.h281
-rw-r--r--toolkit/crashreporter/breakpad-client/minidump_file_writer_unittest.cc179
-rw-r--r--toolkit/crashreporter/breakpad-client/moz.build33
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h81
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h181
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc319
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h78
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild14
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/ReadMe.txt58
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.cc258
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.h182
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.cc406
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.h182
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.cc996
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.h318
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.cc581
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.h203
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild17
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.cc1121
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.h547
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/handler/objs.mozbuild17
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.cc140
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.h122
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild14
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/crash_generation_server_test.cc303
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.cc184
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.h102
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_death_test.cc587
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_nesting_test.cc327
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.cc503
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.h61
-rw-r--r--toolkit/crashreporter/breakpad-client/windows/unittests/minidump_test.cc332
118 files changed, 36613 insertions, 0 deletions
diff --git a/toolkit/crashreporter/breakpad-client/linux/crash_generation/client_info.h b/toolkit/crashreporter/breakpad-client/linux/crash_generation/client_info.h
new file mode 100644
index 0000000000..3de2606b7b
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/crash_generation/client_info.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
+
+namespace google_breakpad {
+
+class CrashGenerationServer;
+
+class ClientInfo {
+ public:
+ ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
+ : crash_server_(crash_server),
+ pid_(pid) {}
+
+ CrashGenerationServer* crash_server() const { return crash_server_; }
+ pid_t pid() const { return pid_; }
+ void set_error_msg(nsCString &error_msg) {
+ had_error_ = true;
+ error_msg_ = error_msg;
+ }
+
+ const nsCString* error_msg() const {
+ return &error_msg_;
+ }
+
+ bool had_error() const {
+ return had_error_;
+ }
+
+ private:
+ CrashGenerationServer* crash_server_;
+ pid_t pid_;
+ bool had_error_ = false;
+ nsCString error_msg_; // Possible error message of the minidumper in
+ // case there was an error during dumping
+};
+
+}
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.cc b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.cc
new file mode 100644
index 0000000000..02dc66f355
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.cc
@@ -0,0 +1,105 @@
+// 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 "linux/crash_generation/crash_generation_client.h"
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <algorithm>
+
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/ignore_ret.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+namespace {
+
+class CrashGenerationClientImpl : public CrashGenerationClient {
+ public:
+ explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
+ virtual ~CrashGenerationClientImpl() {}
+
+ virtual bool RequestDump(const void* blob, size_t blob_size) {
+ int fds[2];
+ if (sys_pipe(fds) < 0)
+ return false;
+ static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
+
+ struct kernel_iovec iov;
+ iov.iov_base = const_cast<void*>(blob);
+ iov.iov_len = blob_size;
+
+ struct kernel_msghdr msg = { 0 };
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ char cmsg[kControlMsgSize] = "";
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_RIGHTS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(int));
+ int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
+ *p = fds[1];
+
+ ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
+ sys_close(fds[1]);
+ if (ret < 0) {
+ sys_close(fds[0]);
+ return false;
+ }
+
+ // Wait for an ACK from the server.
+ char b;
+ IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
+ sys_close(fds[0]);
+
+ return true;
+ }
+
+ private:
+ int server_fd_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl);
+};
+
+} // namespace
+
+// static
+CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) {
+ if (server_fd < 0)
+ return NULL;
+ return new CrashGenerationClientImpl(server_fd);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.h b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.h
new file mode 100644
index 0000000000..4e68424ae8
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_client.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+
+#include "common/basictypes.h"
+
+#include <stddef.h>
+
+namespace google_breakpad {
+
+// CrashGenerationClient is an interface for implementing out-of-process crash
+// dumping. The default implementation, accessed via the TryCreate() factory,
+// works in conjunction with the CrashGenerationServer to generate a minidump
+// via a remote process.
+class CrashGenerationClient {
+ public:
+ CrashGenerationClient() {}
+ virtual ~CrashGenerationClient() {}
+
+ // Request the crash server to generate a dump. |blob| is an opaque
+ // CrashContext pointer from exception_handler.h.
+ // Returns true if the dump was successful; false otherwise.
+ virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
+
+ // Returns a new CrashGenerationClient if |server_fd| is valid and
+ // connects to a CrashGenerationServer. Otherwise, return NULL.
+ // The returned CrashGenerationClient* is owned by the caller of
+ // this function.
+ static CrashGenerationClient* TryCreate(int server_fd);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.cc
new file mode 100644
index 0000000000..58baa31d10
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.cc
@@ -0,0 +1,383 @@
+// 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 <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "nsThreadUtils.h"
+
+#include "linux/crash_generation/crash_generation_server.h"
+#include "linux/crash_generation/client_info.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/handler/guid_generator.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/safe_readlink.h"
+
+#if defined(MOZ_OXIDIZED_BREAKPAD)
+# include "mozilla/toolkit/crashreporter/rust_minidump_writer_linux_ffi_generated.h"
+# include <sys/signalfd.h>
+# include "nsString.h"
+#endif
+
+static const char kCommandQuit = 'x';
+
+namespace google_breakpad {
+
+CrashGenerationServer::CrashGenerationServer(
+ const int listen_fd,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientExitingCallback exit_callback,
+ void* exit_context,
+ bool generate_dumps,
+ const string* dump_path) :
+ server_fd_(listen_fd),
+ dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ generate_dumps_(generate_dumps),
+ started_(false)
+{
+ if (dump_path)
+ dump_dir_ = *dump_path;
+ else
+ dump_dir_ = "/tmp";
+}
+
+CrashGenerationServer::~CrashGenerationServer()
+{
+ if (started_)
+ Stop();
+}
+
+bool
+CrashGenerationServer::Start()
+{
+ if (started_ || 0 > server_fd_)
+ return false;
+
+ int control_pipe[2];
+ if (pipe(control_pipe))
+ return false;
+
+ if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
+ return false;
+ if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
+ return false;
+
+ if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
+ return false;
+
+ control_pipe_in_ = control_pipe[0];
+ control_pipe_out_ = control_pipe[1];
+
+ if (pthread_create(&thread_, NULL,
+ ThreadMain, reinterpret_cast<void*>(this)))
+ return false;
+
+ started_ = true;
+ return true;
+}
+
+void
+CrashGenerationServer::Stop()
+{
+ assert(pthread_self() != thread_);
+
+ if (!started_)
+ return;
+
+ HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
+
+ void* dummy;
+ pthread_join(thread_, &dummy);
+
+ close(control_pipe_in_);
+ close(control_pipe_out_);
+
+ started_ = false;
+}
+
+//static
+bool
+CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
+{
+ int fds[2];
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
+ return false;
+
+ static const int on = 1;
+ // Enable passcred on the server end of the socket
+ if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
+ return false;
+
+ if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
+ return false;
+ if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
+ return false;
+
+ *client_fd = fds[0];
+ *server_fd = fds[1];
+ return true;
+}
+
+// The following methods/functions execute on the server thread
+
+void
+CrashGenerationServer::Run()
+{
+ struct pollfd pollfds[2];
+ memset(&pollfds, 0, sizeof(pollfds));
+
+ pollfds[0].fd = server_fd_;
+ pollfds[0].events = POLLIN;
+
+ pollfds[1].fd = control_pipe_in_;
+ pollfds[1].events = POLLIN;
+
+ while (true) {
+ // infinite timeout
+ int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
+ if (-1 == nevents) {
+ if (EINTR == errno) {
+ continue;
+ } else {
+ return;
+ }
+ }
+
+ if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
+ return;
+
+ if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
+ return;
+ }
+}
+
+bool
+CrashGenerationServer::ClientEvent(short revents)
+{
+ if (POLLHUP & revents)
+ return false;
+ assert(POLLIN & revents);
+
+ // A process has crashed and has signaled us by writing a datagram
+ // to the death signal socket. The datagram contains the crash context needed
+ // for writing the minidump as well as a file descriptor and a credentials
+ // block so that they can't lie about their pid.
+
+ // The length of the control message:
+ static const unsigned kControlMsgSize =
+ CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+ // The length of the regular payload:
+ static const unsigned kCrashContextSize =
+ sizeof(google_breakpad::ExceptionHandler::CrashContext);
+
+ struct msghdr msg = {0};
+ struct iovec iov[1];
+ char crash_context[kCrashContextSize];
+ char control[kControlMsgSize];
+ const ssize_t expected_msg_size = sizeof(crash_context);
+
+ iov[0].iov_base = crash_context;
+ iov[0].iov_len = sizeof(crash_context);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
+ msg.msg_control = control;
+ msg.msg_controllen = kControlMsgSize;
+
+ const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
+ if (msg_size != expected_msg_size)
+ return true;
+
+ if (msg.msg_controllen != kControlMsgSize ||
+ msg.msg_flags & ~MSG_TRUNC)
+ return true;
+
+ // Walk the control payload and extract the file descriptor and validated pid.
+ pid_t crashing_pid = -1;
+ int signal_fd = -1;
+ for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+ hdr = CMSG_NXTHDR(&msg, hdr)) {
+ if (hdr->cmsg_level != SOL_SOCKET)
+ continue;
+ if (hdr->cmsg_type == SCM_RIGHTS) {
+ const unsigned len = hdr->cmsg_len -
+ (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+ assert(len % sizeof(int) == 0u);
+ const unsigned num_fds = len / sizeof(int);
+ if (num_fds > 1 || num_fds == 0) {
+ // A nasty process could try and send us too many descriptors and
+ // force a leak.
+ for (unsigned i = 0; i < num_fds; ++i)
+ close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
+ return true;
+ } else {
+ signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
+ }
+ } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+ const struct ucred *cred =
+ reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ crashing_pid = cred->pid;
+ }
+ }
+
+ if (crashing_pid == -1 || signal_fd == -1) {
+ if (signal_fd != -1)
+ close(signal_fd);
+ return true;
+ }
+
+ string minidump_filename;
+ if (!MakeMinidumpFilename(minidump_filename))
+ return true;
+
+#if defined(MOZ_OXIDIZED_BREAKPAD)
+ ExceptionHandler::CrashContext* breakpad_cc =
+ reinterpret_cast<ExceptionHandler::CrashContext*>(crash_context);
+ nsCString error_msg;
+ siginfo_t& si = breakpad_cc->siginfo;
+ signalfd_siginfo signalfd_si = {};
+ signalfd_si.ssi_signo = si.si_signo;
+ signalfd_si.ssi_errno = si.si_errno;
+ signalfd_si.ssi_code = si.si_code;
+
+ switch (si.si_signo) {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGSYS:
+ signalfd_si.ssi_addr = reinterpret_cast<size_t>(si.si_addr);
+ break;
+ }
+
+ // Ignoring the return-value here for now.
+ // The function always creates an empty minidump file even in case of an
+ // error. So we'll report that as well via the callback-functions.
+ bool res = write_minidump_linux_with_context(
+ minidump_filename.c_str(), crashing_pid, &breakpad_cc->context,
+# ifndef __arm__
+ reinterpret_cast<const fpregset_t *>(&breakpad_cc->float_state),
+# else
+ nullptr,
+# endif // __arm__
+ &signalfd_si, breakpad_cc->tid, &error_msg);
+#else
+ if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
+ crashing_pid, crash_context,
+ kCrashContextSize)) {
+ close(signal_fd);
+ return true;
+ }
+#endif
+
+ ClientInfo info(crashing_pid, this);
+#if defined(MOZ_OXIDIZED_BREAKPAD)
+ if (!res) {
+ info.set_error_msg(error_msg);
+ }
+#endif
+ if (dump_callback_) {
+ dump_callback_(dump_context_, info, minidump_filename);
+ }
+
+ // Send the done signal to the process: it can exit now.
+ // (Closing this will make the child's sys_read unblock and return 0.)
+ close(signal_fd);
+
+ if (exit_callback_) {
+ exit_callback_(exit_context_, info);
+ }
+
+ return true;
+}
+
+bool
+CrashGenerationServer::ControlEvent(short revents)
+{
+ if (POLLHUP & revents)
+ return false;
+ assert(POLLIN & revents);
+
+ char command;
+ if (read(control_pipe_in_, &command, 1))
+ return false;
+
+ switch (command) {
+ case kCommandQuit:
+ return false;
+ default:
+ assert(0);
+ }
+
+ return true;
+}
+
+bool
+CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
+{
+ GUID guid;
+ char guidString[kGUIDStringLength+1];
+
+ if (!(CreateGUID(&guid)
+ && GUIDToString(&guid, guidString, sizeof(guidString))))
+ return false;
+
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
+
+ outFilename = path;
+ return true;
+}
+
+// static
+void*
+CrashGenerationServer::ThreadMain(void *arg)
+{
+ NS_SetCurrentThreadName("Breakpad Server");
+ reinterpret_cast<CrashGenerationServer*>(arg)->Run();
+ return NULL;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.h b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.h
new file mode 100644
index 0000000000..83b626ab1e
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/crash_generation/crash_generation_server.h
@@ -0,0 +1,135 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+
+#include <pthread.h>
+
+#include <string>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+class ClientInfo;
+
+class CrashGenerationServer {
+public:
+ // WARNING: callbacks may be invoked on a different thread
+ // than that which creates the CrashGenerationServer. They must
+ // be thread safe.
+ typedef void (*OnClientDumpRequestCallback)(void* context,
+ const ClientInfo& client_info,
+ const string& file_path);
+
+ typedef void (*OnClientExitingCallback)(void* context,
+ const ClientInfo& client_info);
+
+ // Create an instance with the given parameters.
+ //
+ // Parameter listen_fd: The server fd created by CreateReportChannel().
+ // Parameter dump_callback: Callback for a client crash dump request.
+ // Parameter dump_context: Context for client crash dump request callback.
+ // Parameter exit_callback: Callback for client process exit.
+ // Parameter exit_context: Context for client exit callback.
+ // Parameter generate_dumps: Whether to automatically generate dumps.
+ // Client code of this class might want to generate dumps explicitly
+ // in the crash dump request callback. In that case, false can be
+ // passed for this parameter.
+ // Parameter dump_path: Path for generating dumps; required only if true is
+ // passed for generateDumps parameter; NULL can be passed otherwise.
+ CrashGenerationServer(const int listen_fd,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientExitingCallback exit_callback,
+ void* exit_context,
+ bool generate_dumps,
+ const string* dump_path);
+
+ ~CrashGenerationServer();
+
+ // Perform initialization steps needed to start listening to clients.
+ //
+ // Return true if initialization is successful; false otherwise.
+ bool Start();
+
+ // Stop the server.
+ void Stop();
+
+ // Create a "channel" that can be used by clients to report crashes
+ // to a CrashGenerationServer. |*server_fd| should be passed to
+ // this class's constructor, and |*client_fd| should be passed to
+ // the ExceptionHandler constructor in the client process.
+ static bool CreateReportChannel(int* server_fd, int* client_fd);
+
+private:
+ // Run the server's event loop
+ void Run();
+
+ // Invoked when an child process (client) event occurs
+ // Returning true => "keep running", false => "exit loop"
+ bool ClientEvent(short revents);
+
+ // Invoked when the controlling thread (main) event occurs
+ // Returning true => "keep running", false => "exit loop"
+ bool ControlEvent(short revents);
+
+ // Return a unique filename at which a minidump can be written
+ bool MakeMinidumpFilename(string& outFilename);
+
+ // Trampoline to |Run()|
+ static void* ThreadMain(void* arg);
+
+ int server_fd_;
+
+ OnClientDumpRequestCallback dump_callback_;
+ void* dump_context_;
+
+ OnClientExitingCallback exit_callback_;
+ void* exit_context_;
+
+ bool generate_dumps_;
+
+ string dump_dir_;
+
+ bool started_;
+
+ pthread_t thread_;
+ int control_pipe_in_;
+ int control_pipe_out_;
+
+ // disable these
+ CrashGenerationServer(const CrashGenerationServer&);
+ CrashGenerationServer& operator=(const CrashGenerationServer&);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-amd.sym b/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-amd.sym
new file mode 100644
index 0000000000..e042a5ec42
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-amd.sym
@@ -0,0 +1,3 @@
+MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
+PUBLIC 400 0 __kernel_vsyscall
+STACK WIN 4 400 100 1 1 0 0 0 0 0 1
diff --git a/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-intel.sym b/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-intel.sym
new file mode 100644
index 0000000000..c20facaf08
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/data/linux-gate-intel.sym
@@ -0,0 +1,3 @@
+MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
+PUBLIC 400 0 __kernel_vsyscall
+STACK WIN 4 400 200 3 3 0 0 0 0 0 1
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/mapping_info.h b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/mapping_info.h
new file mode 100644
index 0000000000..c358539f58
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/mapping_info.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
+#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
+
+#include <limits.h>
+#include <list>
+#include <stdint.h>
+#include <vector>
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// One of these is produced for each mapping in the process (i.e. line in
+// /proc/$x/maps).
+struct MappingInfo {
+ // On Android, relocation packing can mean that the reported start
+ // address of the mapping must be adjusted by a bias in order to
+ // compensate for the compression of the relocation section. The
+ // following two members hold (after LateInit) the adjusted mapping
+ // range. See crbug.com/606972 for more information.
+ uintptr_t start_addr;
+ size_t size;
+ // When Android relocation packing causes |start_addr| and |size| to
+ // be modified with a load bias, we need to remember the unbiased
+ // address range. The following structure holds the original mapping
+ // address range as reported by the operating system.
+ struct {
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+ } system_mapping_info;
+ size_t offset; // offset into the backed file.
+ bool exec; // true if the mapping has the execute bit set.
+ char name[NAME_MAX];
+};
+
+struct MappingEntry {
+ MappingInfo first;
+ std::vector<uint8_t> second;
+};
+
+// A list of <MappingInfo, GUID>
+typedef std::list<MappingEntry> MappingList;
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/raw_context_cpu.h b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/raw_context_cpu.h
new file mode 100644
index 0000000000..07d9171a0a
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/raw_context_cpu.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
+#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+#if defined(__i386__)
+typedef MDRawContextX86 RawContextCPU;
+#elif defined(__x86_64)
+typedef MDRawContextAMD64 RawContextCPU;
+#elif defined(__ARM_EABI__)
+typedef MDRawContextARM RawContextCPU;
+#elif defined(__aarch64__)
+typedef MDRawContextARM64_Old RawContextCPU;
+#elif defined(__mips__)
+typedef MDRawContextMIPS RawContextCPU;
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.cc b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.cc
new file mode 100644
index 0000000000..5d9708c70c
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.cc
@@ -0,0 +1,305 @@
+// 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.
+
+#include "linux/dump_writer_common/thread_info.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "common/linux/linux_libc_support.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace {
+
+#if defined(__i386__)
+// Write a uint16_t to memory
+// out: memory location to write to
+// v: value to write.
+void U16(void* out, uint16_t v) {
+ my_memcpy(out, &v, sizeof(v));
+}
+
+// Write a uint32_t to memory
+// out: memory location to write to
+// v: value to write.
+void U32(void* out, uint32_t v) {
+ my_memcpy(out, &v, sizeof(v));
+}
+#endif
+
+}
+
+namespace google_breakpad {
+
+#if defined(__i386__)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return regs.eip;
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+ out->context_flags = MD_CONTEXT_X86_ALL;
+
+ out->dr0 = dregs[0];
+ out->dr1 = dregs[1];
+ out->dr2 = dregs[2];
+ out->dr3 = dregs[3];
+ // 4 and 5 deliberatly omitted because they aren't included in the minidump
+ // format.
+ out->dr6 = dregs[6];
+ out->dr7 = dregs[7];
+
+ out->gs = regs.xgs;
+ out->fs = regs.xfs;
+ out->es = regs.xes;
+ out->ds = regs.xds;
+
+ out->edi = regs.edi;
+ out->esi = regs.esi;
+ out->ebx = regs.ebx;
+ out->edx = regs.edx;
+ out->ecx = regs.ecx;
+ out->eax = regs.eax;
+
+ out->ebp = regs.ebp;
+ out->eip = regs.eip;
+ out->cs = regs.xcs;
+ out->eflags = regs.eflags;
+ out->esp = regs.esp;
+ out->ss = regs.xss;
+
+ out->float_save.control_word = fpregs.cwd;
+ out->float_save.status_word = fpregs.swd;
+ out->float_save.tag_word = fpregs.twd;
+ out->float_save.error_offset = fpregs.fip;
+ out->float_save.error_selector = fpregs.fcs;
+ out->float_save.data_offset = fpregs.foo;
+ out->float_save.data_selector = fpregs.fos;
+
+ // 8 registers * 10 bytes per register.
+ my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
+
+ // This matches the Intel fpsave format.
+ U16(out->extended_registers + 0, fpregs.cwd);
+ U16(out->extended_registers + 2, fpregs.swd);
+ U16(out->extended_registers + 4, fpregs.twd);
+ U16(out->extended_registers + 6, fpxregs.fop);
+ U32(out->extended_registers + 8, fpxregs.fip);
+ U16(out->extended_registers + 12, fpxregs.fcs);
+ U32(out->extended_registers + 16, fpregs.foo);
+ U16(out->extended_registers + 20, fpregs.fos);
+ U32(out->extended_registers + 24, fpxregs.mxcsr);
+
+ my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
+ my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
+}
+
+#elif defined(__x86_64)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return regs.rip;
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+ out->context_flags = MD_CONTEXT_AMD64_FULL |
+ MD_CONTEXT_AMD64_SEGMENTS;
+
+ out->cs = regs.cs;
+
+ out->ds = regs.ds;
+ out->es = regs.es;
+ out->fs = regs.fs;
+ out->gs = regs.gs;
+
+ out->ss = regs.ss;
+ out->eflags = regs.eflags;
+
+ out->dr0 = dregs[0];
+ out->dr1 = dregs[1];
+ out->dr2 = dregs[2];
+ out->dr3 = dregs[3];
+ // 4 and 5 deliberatly omitted because they aren't included in the minidump
+ // format.
+ out->dr6 = dregs[6];
+ out->dr7 = dregs[7];
+
+ out->rax = regs.rax;
+ out->rcx = regs.rcx;
+ out->rdx = regs.rdx;
+ out->rbx = regs.rbx;
+
+ out->rsp = regs.rsp;
+
+ out->rbp = regs.rbp;
+ out->rsi = regs.rsi;
+ out->rdi = regs.rdi;
+ out->r8 = regs.r8;
+ out->r9 = regs.r9;
+ out->r10 = regs.r10;
+ out->r11 = regs.r11;
+ out->r12 = regs.r12;
+ out->r13 = regs.r13;
+ out->r14 = regs.r14;
+ out->r15 = regs.r15;
+
+ out->rip = regs.rip;
+
+ out->flt_save.control_word = fpregs.cwd;
+ out->flt_save.status_word = fpregs.swd;
+ out->flt_save.tag_word = fpregs.ftw;
+ out->flt_save.error_opcode = fpregs.fop;
+ out->flt_save.error_offset = fpregs.rip;
+ out->flt_save.error_selector = 0; // We don't have this.
+ out->flt_save.data_offset = fpregs.rdp;
+ out->flt_save.data_selector = 0; // We don't have this.
+ out->flt_save.mx_csr = fpregs.mxcsr;
+ out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
+
+ my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
+ my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
+}
+
+#elif defined(__ARM_EABI__)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return regs.uregs[15];
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+ out->context_flags = MD_CONTEXT_ARM_FULL;
+
+ for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
+ out->iregs[i] = regs.uregs[i];
+ // No CPSR register in ThreadInfo(it's not accessible via ptrace)
+ out->cpsr = 0;
+#if !defined(__ANDROID__)
+ out->float_save.fpscr = fpregs.fpsr |
+ (static_cast<uint64_t>(fpregs.fpcr) << 32);
+ // TODO: sort this out, actually collect floating point registers
+ my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
+ my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
+#endif
+}
+
+#elif defined(__aarch64__)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return regs.pc;
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+ out->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
+
+ out->cpsr = static_cast<uint32_t>(regs.pstate);
+ for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
+ out->iregs[i] = regs.regs[i];
+ out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp;
+ out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc;
+
+ out->float_save.fpsr = fpregs.fpsr;
+ out->float_save.fpcr = fpregs.fpcr;
+ my_memcpy(&out->float_save.regs, &fpregs.vregs,
+ MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
+}
+
+#elif defined(__mips__)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return mcontext.pc;
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+#if _MIPS_SIM == _ABI64
+ out->context_flags = MD_CONTEXT_MIPS64_FULL;
+#elif _MIPS_SIM == _ABIO32
+ out->context_flags = MD_CONTEXT_MIPS_FULL;
+#else
+# error "This mips ABI is currently not supported (n32)"
+#endif
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
+ out->iregs[i] = mcontext.gregs[i];
+
+ out->mdhi = mcontext.mdhi;
+ out->mdlo = mcontext.mdlo;
+ out->dsp_control = mcontext.dsp;
+
+ out->hi[0] = mcontext.hi1;
+ out->lo[0] = mcontext.lo1;
+ out->hi[1] = mcontext.hi2;
+ out->lo[1] = mcontext.lo2;
+ out->hi[2] = mcontext.hi3;
+ out->lo[2] = mcontext.lo3;
+
+ out->epc = mcontext.pc;
+ out->badvaddr = 0; // Not stored in mcontext
+ out->status = 0; // Not stored in mcontext
+ out->cause = 0; // Not stored in mcontext
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
+ out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs;
+
+ out->float_save.fpcsr = mcontext.fpc_csr;
+#if _MIPS_SIM == _ABIO32
+ out->float_save.fir = mcontext.fpc_eir;
+#endif
+}
+#endif // __mips__
+
+void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
+ assert(gp_regs || size);
+#if defined(__mips__)
+ if (gp_regs)
+ *gp_regs = mcontext.gregs;
+ if (size)
+ *size = sizeof(mcontext.gregs);
+#else
+ if (gp_regs)
+ *gp_regs = &regs;
+ if (size)
+ *size = sizeof(regs);
+#endif
+}
+
+void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
+ assert(fp_regs || size);
+#if defined(__mips__)
+ if (fp_regs)
+ *fp_regs = &mcontext.fpregs;
+ if (size)
+ *size = sizeof(mcontext.fpregs);
+#else
+ if (fp_regs)
+ *fp_regs = &fpregs;
+ if (size)
+ *size = sizeof(fpregs);
+#endif
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.h b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.h
new file mode 100644
index 0000000000..4173d239c4
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/thread_info.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
+#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
+
+#include <sys/ucontext.h>
+#include <sys/user.h>
+
+#include "linux/dump_writer_common/raw_context_cpu.h"
+#include "common/memory_allocator.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+#if defined(__i386) || defined(__x86_64)
+typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
+#endif
+
+// We produce one of these structures for each thread in the crashed process.
+struct ThreadInfo {
+ pid_t tgid; // thread group id
+ pid_t ppid; // parent process
+
+ uintptr_t stack_pointer; // thread stack pointer
+
+
+#if defined(__i386) || defined(__x86_64)
+ user_regs_struct regs;
+ user_fpregs_struct fpregs;
+ static const unsigned kNumDebugRegisters = 8;
+ debugreg_t dregs[8];
+#if defined(__i386)
+ user_fpxregs_struct fpxregs;
+#endif // defined(__i386)
+
+#elif defined(__ARM_EABI__)
+ // Mimicking how strace does this(see syscall.c, search for GETREGS)
+ struct user_regs regs;
+ struct user_fpregs fpregs;
+#elif defined(__aarch64__)
+ // Use the structures defined in <sys/user.h>
+ struct user_regs_struct regs;
+ struct user_fpsimd_struct fpregs;
+#elif defined(__mips__)
+ // Use the structure defined in <sys/ucontext.h>.
+ mcontext_t mcontext;
+#endif
+
+ // Returns the instruction pointer (platform-dependent impl.).
+ uintptr_t GetInstructionPointer() const;
+
+ // Fills a RawContextCPU using the context in the ThreadInfo object.
+ void FillCPUContext(RawContextCPU* out) const;
+
+ // Returns the pointer and size of general purpose register area.
+ void GetGeneralPurposeRegisters(void** gp_regs, size_t* size);
+
+ // Returns the pointer and size of float point register area.
+ void GetFloatingPointRegisters(void** fp_regs, size_t* size);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.cc b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.cc
new file mode 100644
index 0000000000..5145ede3d8
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.cc
@@ -0,0 +1,259 @@
+// 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.
+
+#include "linux/dump_writer_common/ucontext_reader.h"
+
+#include "common/linux/linux_libc_support.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// Minidump defines register structures which are different from the raw
+// structures which we get from the kernel. These are platform specific
+// functions to juggle the ucontext_t and user structures into minidump format.
+
+#if defined(__i386__)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.gregs[REG_ESP];
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.gregs[REG_EIP];
+}
+
+void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
+ const fpstate_t* fp) {
+ const greg_t* regs = uc->uc_mcontext.gregs;
+
+ out->context_flags = MD_CONTEXT_X86_FULL |
+ MD_CONTEXT_X86_FLOATING_POINT;
+
+ out->gs = regs[REG_GS];
+ out->fs = regs[REG_FS];
+ out->es = regs[REG_ES];
+ out->ds = regs[REG_DS];
+
+ out->edi = regs[REG_EDI];
+ out->esi = regs[REG_ESI];
+ out->ebx = regs[REG_EBX];
+ out->edx = regs[REG_EDX];
+ out->ecx = regs[REG_ECX];
+ out->eax = regs[REG_EAX];
+
+ out->ebp = regs[REG_EBP];
+ out->eip = regs[REG_EIP];
+ out->cs = regs[REG_CS];
+ out->eflags = regs[REG_EFL];
+ out->esp = regs[REG_UESP];
+ out->ss = regs[REG_SS];
+
+ out->float_save.control_word = fp->cw;
+ out->float_save.status_word = fp->sw;
+ out->float_save.tag_word = fp->tag;
+ out->float_save.error_offset = fp->ipoff;
+ out->float_save.error_selector = fp->cssel;
+ out->float_save.data_offset = fp->dataoff;
+ out->float_save.data_selector = fp->datasel;
+
+ // 8 registers * 10 bytes per register.
+ my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
+}
+
+#elif defined(__x86_64)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.gregs[REG_RSP];
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.gregs[REG_RIP];
+}
+
+void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
+ const fpstate_t* fpregs) {
+ const greg_t* regs = uc->uc_mcontext.gregs;
+
+ out->context_flags = MD_CONTEXT_AMD64_FULL;
+
+ out->cs = regs[REG_CSGSFS] & 0xffff;
+
+ out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
+ out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
+
+ out->eflags = regs[REG_EFL];
+
+ out->rax = regs[REG_RAX];
+ out->rcx = regs[REG_RCX];
+ out->rdx = regs[REG_RDX];
+ out->rbx = regs[REG_RBX];
+
+ out->rsp = regs[REG_RSP];
+ out->rbp = regs[REG_RBP];
+ out->rsi = regs[REG_RSI];
+ out->rdi = regs[REG_RDI];
+ out->r8 = regs[REG_R8];
+ out->r9 = regs[REG_R9];
+ out->r10 = regs[REG_R10];
+ out->r11 = regs[REG_R11];
+ out->r12 = regs[REG_R12];
+ out->r13 = regs[REG_R13];
+ out->r14 = regs[REG_R14];
+ out->r15 = regs[REG_R15];
+
+ out->rip = regs[REG_RIP];
+
+ out->flt_save.control_word = fpregs->cwd;
+ out->flt_save.status_word = fpregs->swd;
+ out->flt_save.tag_word = fpregs->ftw;
+ out->flt_save.error_opcode = fpregs->fop;
+ out->flt_save.error_offset = fpregs->rip;
+ out->flt_save.data_offset = fpregs->rdp;
+ out->flt_save.error_selector = 0; // We don't have this.
+ out->flt_save.data_selector = 0; // We don't have this.
+ out->flt_save.mx_csr = fpregs->mxcsr;
+ out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
+ my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
+ my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
+}
+
+#elif defined(__ARM_EABI__)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.arm_sp;
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.arm_pc;
+}
+
+void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) {
+ out->context_flags = MD_CONTEXT_ARM_FULL;
+
+ out->iregs[0] = uc->uc_mcontext.arm_r0;
+ out->iregs[1] = uc->uc_mcontext.arm_r1;
+ out->iregs[2] = uc->uc_mcontext.arm_r2;
+ out->iregs[3] = uc->uc_mcontext.arm_r3;
+ out->iregs[4] = uc->uc_mcontext.arm_r4;
+ out->iregs[5] = uc->uc_mcontext.arm_r5;
+ out->iregs[6] = uc->uc_mcontext.arm_r6;
+ out->iregs[7] = uc->uc_mcontext.arm_r7;
+ out->iregs[8] = uc->uc_mcontext.arm_r8;
+ out->iregs[9] = uc->uc_mcontext.arm_r9;
+ out->iregs[10] = uc->uc_mcontext.arm_r10;
+
+ out->iregs[11] = uc->uc_mcontext.arm_fp;
+ out->iregs[12] = uc->uc_mcontext.arm_ip;
+ out->iregs[13] = uc->uc_mcontext.arm_sp;
+ out->iregs[14] = uc->uc_mcontext.arm_lr;
+ out->iregs[15] = uc->uc_mcontext.arm_pc;
+
+ out->cpsr = uc->uc_mcontext.arm_cpsr;
+
+ // TODO: fix this after fixing ExceptionHandler
+ out->float_save.fpscr = 0;
+ my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
+ my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
+}
+
+#elif defined(__aarch64__)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.sp;
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.pc;
+}
+
+void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
+ const struct fpsimd_context* fpregs) {
+ out->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
+
+ out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate);
+ for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
+ out->iregs[i] = uc->uc_mcontext.regs[i];
+ out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp;
+ out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc;
+
+ out->float_save.fpsr = fpregs->fpsr;
+ out->float_save.fpcr = fpregs->fpcr;
+ my_memcpy(&out->float_save.regs, &fpregs->vregs,
+ MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
+}
+
+#elif defined(__mips__)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.pc;
+}
+
+void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) {
+#if _MIPS_SIM == _ABI64
+ out->context_flags = MD_CONTEXT_MIPS64_FULL;
+#elif _MIPS_SIM == _ABIO32
+ out->context_flags = MD_CONTEXT_MIPS_FULL;
+#else
+#error "This mips ABI is currently not supported (n32)"
+#endif
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
+ out->iregs[i] = uc->uc_mcontext.gregs[i];
+
+ out->mdhi = uc->uc_mcontext.mdhi;
+ out->mdlo = uc->uc_mcontext.mdlo;
+
+ out->hi[0] = uc->uc_mcontext.hi1;
+ out->hi[1] = uc->uc_mcontext.hi2;
+ out->hi[2] = uc->uc_mcontext.hi3;
+ out->lo[0] = uc->uc_mcontext.lo1;
+ out->lo[1] = uc->uc_mcontext.lo2;
+ out->lo[2] = uc->uc_mcontext.lo3;
+ out->dsp_control = uc->uc_mcontext.dsp;
+
+ out->epc = uc->uc_mcontext.pc;
+ out->badvaddr = 0; // Not reported in signal context.
+ out->status = 0; // Not reported in signal context.
+ out->cause = 0; // Not reported in signal context.
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
+ out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
+
+ out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
+#if _MIPS_SIM == _ABIO32
+ out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
+#endif
+}
+#endif
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.h b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.h
new file mode 100644
index 0000000000..390520be41
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/dump_writer_common/ucontext_reader.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
+#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
+
+#include <sys/ucontext.h>
+#include <sys/user.h>
+
+#include "linux/dump_writer_common/raw_context_cpu.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "common/memory_allocator.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// Wraps platform-dependent implementations of accessors to ucontext_t structs.
+struct UContextReader {
+ static uintptr_t GetStackPointer(const ucontext_t* uc);
+
+ static uintptr_t GetInstructionPointer(const ucontext_t* uc);
+
+ // Juggle a arch-specific ucontext into a minidump format
+ // out: the minidump structure
+ // info: the collection of register structures.
+#if defined(__i386__) || defined(__x86_64)
+ static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
+ const fpstate_t* fp);
+#elif defined(__aarch64__)
+ static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
+ const struct fpsimd_context* fpregs);
+#else
+ static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc);
+#endif
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.cc b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.cc
new file mode 100644
index 0000000000..92a0443d79
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.cc
@@ -0,0 +1,876 @@
+// 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.
+
+// The ExceptionHandler object installs signal handlers for a number of
+// signals. We rely on the signal handler running on the thread which crashed
+// in order to identify it. This is true of the synchronous signals (SEGV etc),
+// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
+// uses ExceptionHandler, you need to use tgkill to direct it to the current
+// thread.
+//
+// The signal flow looks like this:
+//
+// SignalHandler (uses a global stack of ExceptionHandler objects to find
+// | one to handle the signal. If the first rejects it, try
+// | the second etc...)
+// V
+// HandleSignal ----------------------------| (clones a new process which
+// | | shares an address space with
+// (wait for cloned | the crashed process. This
+// process) | allows us to ptrace the crashed
+// | | process)
+// V V
+// (set signal handler to ThreadEntry (static function to bounce
+// SIG_DFL and rethrow, | back into the object)
+// killing the crashed |
+// process) V
+// DoDump (writes minidump)
+// |
+// V
+// sys_exit
+//
+
+// This code is a little fragmented. Different functions of the ExceptionHandler
+// class run in a number of different contexts. Some of them run in a normal
+// context and are easy to code, others run in a compromised context and the
+// restrictions at the top of minidump_writer.cc apply: no libc and use the
+// alternative malloc. Each function should have comment above it detailing the
+// context which it runs in.
+
+#include "linux/handler/exception_handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <sys/ucontext.h>
+#include <sys/user.h>
+#include <ucontext.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "common/basictypes.h"
+#include "common/linux/breakpad_getcontext.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/memory_allocator.h"
+#include "linux/log/log.h"
+#include "linux/microdump_writer/microdump_writer.h"
+#include "linux/minidump_writer/linux_dumper.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "common/linux/eintr_wrapper.h"
+#include "third_party/lss/linux_syscall_support.h"
+#if defined(MOZ_OXIDIZED_BREAKPAD)
+#include "nsString.h"
+#include "mozilla/toolkit/crashreporter/rust_minidump_writer_linux_ffi_generated.h"
+#endif
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#endif
+
+#if defined(__ANDROID__)
+#include "linux/sched.h"
+#endif
+
+#ifndef PR_SET_PTRACER
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+#define SKIP_SIGILL(sig) if (g_skip_sigill_ && (sig == SIGILL)) continue;
+
+namespace google_breakpad {
+
+namespace {
+// The list of signals which we consider to be crashes. The default action for
+// all these signals must be Core (see man 7 signal) because we rethrow the
+// signal after handling it and expect that it'll be fatal.
+const int kExceptionSignals[] = {
+ SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP
+};
+const int kNumHandledSignals =
+ sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
+struct sigaction old_handlers[kNumHandledSignals];
+bool handlers_installed = false;
+
+// InstallAlternateStackLocked will store the newly installed stack in new_stack
+// and (if it exists) the previously installed stack in old_stack.
+stack_t old_stack;
+stack_t new_stack;
+bool stack_installed = false;
+
+// Create an alternative stack to run the signal handlers on. This is done since
+// the signal might have been caused by a stack overflow.
+// Runs before crashing: normal context.
+void InstallAlternateStackLocked() {
+ if (stack_installed)
+ return;
+
+ memset(&old_stack, 0, sizeof(old_stack));
+ memset(&new_stack, 0, sizeof(new_stack));
+
+ // SIGSTKSZ may be too small to prevent the signal handlers from overrunning
+ // the alternative stack. Ensure that the size of the alternative stack is
+ // large enough.
+ static const size_t kSigStackSize = std::max(size_t(16384), size_t(SIGSTKSZ));
+
+ // Only set an alternative stack if there isn't already one, or if the current
+ // one is too small.
+ if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
+ old_stack.ss_size < kSigStackSize) {
+ new_stack.ss_sp = calloc(1, kSigStackSize);
+ new_stack.ss_size = kSigStackSize;
+
+ if (sys_sigaltstack(&new_stack, NULL) == -1) {
+ free(new_stack.ss_sp);
+ return;
+ }
+ stack_installed = true;
+ }
+}
+
+// Runs before crashing: normal context.
+void RestoreAlternateStackLocked() {
+ if (!stack_installed)
+ return;
+
+ stack_t current_stack;
+ if (sys_sigaltstack(NULL, &current_stack) == -1)
+ return;
+
+ // Only restore the old_stack if the current alternative stack is the one
+ // installed by the call to InstallAlternateStackLocked.
+ if (current_stack.ss_sp == new_stack.ss_sp) {
+ if (old_stack.ss_sp) {
+ if (sys_sigaltstack(&old_stack, NULL) == -1)
+ return;
+ } else {
+ stack_t disable_stack;
+ disable_stack.ss_flags = SS_DISABLE;
+ if (sys_sigaltstack(&disable_stack, NULL) == -1)
+ return;
+ }
+ }
+
+ free(new_stack.ss_sp);
+ stack_installed = false;
+}
+
+void InstallDefaultHandler(int sig) {
+#if defined(__ANDROID__)
+ // Android L+ expose signal and sigaction symbols that override the system
+ // ones. There is a bug in these functions where a request to set the handler
+ // to SIG_DFL is ignored. In that case, an infinite loop is entered as the
+ // signal is repeatedly sent to breakpad's signal handler.
+ // To work around this, directly call the system's sigaction.
+ struct kernel_sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sys_sigemptyset(&sa.sa_mask);
+ sa.sa_handler_ = SIG_DFL;
+ sa.sa_flags = SA_RESTART;
+ sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t));
+#else
+ signal(sig, SIG_DFL);
+#endif
+}
+
+// The global exception handler stack. This is needed because there may exist
+// multiple ExceptionHandler instances in a process. Each will have itself
+// registered in this stack.
+std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
+pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+
+// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack
+// for SignalHandler(). Keep the crash context as a .bss field. Exception
+// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a
+// time can use |g_crash_context_|.
+ExceptionHandler::CrashContext g_crash_context_;
+
+FirstChanceHandler g_first_chance_handler_ = nullptr;
+bool g_skip_sigill_ = false;
+} // namespace
+
+// Runs before crashing: normal context.
+ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ bool install_handler,
+ const int server_fd)
+ : filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ minidump_descriptor_(descriptor),
+ crash_handler_(NULL) {
+
+ g_skip_sigill_ = getenv("MOZ_DISABLE_EXCEPTION_HANDLER_SIGILL") ? true : false;
+ if (server_fd >= 0)
+ crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
+
+ if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
+ !minidump_descriptor_.IsMicrodumpOnConsole())
+ minidump_descriptor_.UpdatePath();
+
+#if defined(__ANDROID__)
+ if (minidump_descriptor_.IsMicrodumpOnConsole())
+ logger::initializeCrashLogWriter();
+#endif
+
+ pthread_mutex_lock(&g_handler_stack_mutex_);
+
+ // Pre-fault the crash context struct. This is to avoid failing due to OOM
+ // if handling an exception when the process ran out of virtual memory.
+ memset(&g_crash_context_, 0, sizeof(g_crash_context_));
+
+ if (!g_handler_stack_)
+ g_handler_stack_ = new std::vector<ExceptionHandler*>;
+ if (install_handler) {
+ InstallAlternateStackLocked();
+ InstallHandlersLocked();
+ }
+ g_handler_stack_->push_back(this);
+ pthread_mutex_unlock(&g_handler_stack_mutex_);
+}
+
+// Runs before crashing: normal context.
+ExceptionHandler::~ExceptionHandler() {
+ pthread_mutex_lock(&g_handler_stack_mutex_);
+ std::vector<ExceptionHandler*>::iterator handler =
+ std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this);
+ g_handler_stack_->erase(handler);
+ if (g_handler_stack_->empty()) {
+ delete g_handler_stack_;
+ g_handler_stack_ = NULL;
+ RestoreAlternateStackLocked();
+ RestoreHandlersLocked();
+ }
+ pthread_mutex_unlock(&g_handler_stack_mutex_);
+}
+
+// Runs before crashing: normal context.
+// static
+bool ExceptionHandler::InstallHandlersLocked() {
+ if (handlers_installed)
+ return false;
+
+ // Fail if unable to store all the old handlers.
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ SKIP_SIGILL(kExceptionSignals[i]);
+ if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
+ return false;
+ }
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+
+ // Mask all exception signals when we're handling one of them.
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ SKIP_SIGILL(kExceptionSignals[i]);
+ sigaddset(&sa.sa_mask, kExceptionSignals[i]);
+ }
+
+ sa.sa_sigaction = SignalHandler;
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ SKIP_SIGILL(kExceptionSignals[i]);
+ if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
+ // At this point it is impractical to back out changes, and so failure to
+ // install a signal is intentionally ignored.
+ }
+ }
+ handlers_installed = true;
+ return true;
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+// static
+void ExceptionHandler::RestoreHandlersLocked() {
+ if (!handlers_installed)
+ return;
+
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ SKIP_SIGILL(kExceptionSignals[i]);
+ if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
+ InstallDefaultHandler(kExceptionSignals[i]);
+ }
+ }
+ handlers_installed = false;
+}
+
+// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
+// crash_handler_ = callback;
+// }
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+// static
+void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
+
+ // Give the first chance handler a chance to recover from this signal
+ //
+ // This is primarily used by V8. V8 uses guard regions to guarantee memory
+ // safety in WebAssembly. This means some signals might be expected if they
+ // originate from Wasm code while accessing the guard region. We give V8 the
+ // chance to handle and recover from these signals first.
+ if (g_first_chance_handler_ != nullptr &&
+ g_first_chance_handler_(sig, info, uc)) {
+ return;
+ }
+
+ // All the exception signals are blocked at this point.
+ pthread_mutex_lock(&g_handler_stack_mutex_);
+
+ // Sometimes, Breakpad runs inside a process where some other buggy code
+ // saves and restores signal handlers temporarily with 'signal'
+ // instead of 'sigaction'. This loses the SA_SIGINFO flag associated
+ // with this function. As a consequence, the values of 'info' and 'uc'
+ // become totally bogus, generally inducing a crash.
+ //
+ // The following code tries to detect this case. When it does, it
+ // resets the signal handlers with sigaction + SA_SIGINFO and returns.
+ // This forces the signal to be thrown again, but this time the kernel
+ // will call the function with the right arguments.
+ struct sigaction cur_handler;
+ if (sigaction(sig, NULL, &cur_handler) == 0 &&
+ cur_handler.sa_sigaction == SignalHandler &&
+ (cur_handler.sa_flags & SA_SIGINFO) == 0) {
+ // Reset signal handler with the right flags.
+ sigemptyset(&cur_handler.sa_mask);
+ sigaddset(&cur_handler.sa_mask, sig);
+
+ cur_handler.sa_sigaction = SignalHandler;
+ cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ if (sigaction(sig, &cur_handler, NULL) == -1) {
+ // When resetting the handler fails, try to reset the
+ // default one to avoid an infinite loop here.
+ InstallDefaultHandler(sig);
+ }
+ pthread_mutex_unlock(&g_handler_stack_mutex_);
+ return;
+ }
+
+ bool handled = false;
+ for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) {
+ handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc);
+ }
+
+ // Upon returning from this signal handler, sig will become unmasked and then
+ // it will be retriggered. If one of the ExceptionHandlers handled it
+ // successfully, restore the default handler. Otherwise, restore the
+ // previously installed handler. Then, when the signal is retriggered, it will
+ // be delivered to the appropriate handler.
+ if (handled) {
+ InstallDefaultHandler(sig);
+ } else {
+ RestoreHandlersLocked();
+ }
+
+ pthread_mutex_unlock(&g_handler_stack_mutex_);
+
+ // info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
+ if (info->si_code <= 0 || sig == SIGABRT) {
+ // This signal was triggered by somebody sending us the signal with kill().
+ // In order to retrigger it, we have to queue a new signal by calling
+ // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
+ // due to the kernel sending a SIGABRT from a user request via SysRQ.
+ if (sys_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
+ // If we failed to kill ourselves (e.g. because a sandbox disallows us
+ // to do so), we instead resort to terminating our process. This will
+ // result in an incorrect exit code.
+ _exit(1);
+ }
+ } else {
+ // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
+ // No need to reissue the signal. It will automatically trigger again,
+ // when we return from the signal handler.
+ }
+}
+
+struct ThreadArgument {
+ pid_t pid; // the crashing process
+ const MinidumpDescriptor* minidump_descriptor;
+ ExceptionHandler* handler;
+ const void* context; // a CrashContext structure
+ size_t context_size;
+};
+
+// This is the entry function for the cloned process. We are in a compromised
+// context here: see the top of the file.
+// static
+int ExceptionHandler::ThreadEntry(void *arg) {
+ const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
+
+ // Close the write end of the pipe. This allows us to fail if the parent dies
+ // while waiting for the continue signal.
+ sys_close(thread_arg->handler->fdes[1]);
+
+ // Block here until the crashing process unblocks us when
+ // we're allowed to use ptrace
+ thread_arg->handler->WaitForContinueSignal();
+ sys_close(thread_arg->handler->fdes[0]);
+
+ return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
+ thread_arg->context_size) == false;
+}
+
+#ifdef MOZ_PHC
+static void GetPHCAddrInfo(siginfo_t* siginfo,
+ mozilla::phc::AddrInfo* addr_info) {
+ // Is this a crash involving a PHC allocation?
+ if (siginfo->si_signo == SIGSEGV || siginfo->si_signo == SIGBUS) {
+ mozilla::phc::IsPHCAllocation(siginfo->si_addr, addr_info);
+ }
+}
+#endif
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) {
+ mozilla::phc::AddrInfo* addr_info = nullptr;
+#ifdef MOZ_PHC
+ addr_info = &mozilla::phc::gAddrInfo;
+ GetPHCAddrInfo(info, addr_info);
+#endif
+
+ if (filter_ && !filter_(callback_context_))
+ return false;
+
+ // Allow ourselves to be dumped if the signal is trusted.
+ bool signal_trusted = info->si_code > 0;
+ bool signal_pid_trusted = info->si_code == SI_USER ||
+ info->si_code == SI_TKILL;
+ if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
+ sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
+
+ // Fill in all the holes in the struct to make Valgrind happy.
+ memset(&g_crash_context_, 0, sizeof(g_crash_context_));
+ memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t));
+ memcpy(&g_crash_context_.context, uc, sizeof(ucontext_t));
+#if defined(__aarch64__)
+ ucontext_t* uc_ptr = (ucontext_t*)uc;
+ struct fpsimd_context* fp_ptr =
+ (struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
+ if (fp_ptr->head.magic == FPSIMD_MAGIC) {
+ memcpy(&g_crash_context_.float_state, fp_ptr,
+ sizeof(g_crash_context_.float_state));
+ }
+#elif !defined(__ARM_EABI__) && !defined(__mips__)
+ // FP state is not part of user ABI on ARM Linux.
+ // In case of MIPS Linux FP state is already part of ucontext_t
+ // and 'float_state' is not a member of CrashContext.
+ ucontext_t* uc_ptr = (ucontext_t*)uc;
+ if (uc_ptr->uc_mcontext.fpregs) {
+ memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
+ sizeof(g_crash_context_.float_state));
+ }
+#endif
+ g_crash_context_.tid = syscall(__NR_gettid);
+ if (crash_handler_ != NULL) {
+ if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_),
+ callback_context_)) {
+ return true;
+ }
+ }
+
+ return GenerateDump(&g_crash_context_, addr_info);
+}
+
+// This is a public interface to HandleSignal that allows the client to
+// generate a crash dump. This function may run in a compromised context.
+bool ExceptionHandler::SimulateSignalDelivery(int sig) {
+ siginfo_t siginfo = {};
+ // Mimic a trusted signal to allow tracing the process (see
+ // ExceptionHandler::HandleSignal().
+ siginfo.si_code = SI_USER;
+ siginfo.si_pid = getpid();
+ ucontext_t context;
+ getcontext(&context);
+ return HandleSignal(sig, &siginfo, &context);
+}
+
+// This function may run in a compromised context: see the top of the file.
+bool ExceptionHandler::GenerateDump(
+ CrashContext *context, const mozilla::phc::AddrInfo* addr_info) {
+ if (IsOutOfProcess()) {
+ bool success =
+ crash_generation_client_->RequestDump(context, sizeof(*context));
+
+ if (callback_) {
+ success =
+ callback_(minidump_descriptor_, callback_context_, addr_info, success);
+ }
+
+ return success;
+ }
+
+ // Allocating too much stack isn't a problem, and better to err on the side
+ // of caution than smash it into random locations.
+ static const unsigned kChildStackSize = 16000;
+ PageAllocator allocator;
+ uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
+ if (!stack)
+ return false;
+ // clone() needs the top-most address. (scrub just to be safe)
+ stack += kChildStackSize;
+ my_memset(stack - 16, 0, 16);
+
+ ThreadArgument thread_arg;
+ thread_arg.handler = this;
+ thread_arg.minidump_descriptor = &minidump_descriptor_;
+ thread_arg.pid = getpid();
+ thread_arg.context = context;
+ thread_arg.context_size = sizeof(*context);
+
+ // We need to explicitly enable ptrace of parent processes on some
+ // kernels, but we need to know the PID of the cloned process before we
+ // can do this. Create a pipe here which we can use to block the
+ // cloned process after creating it, until we have explicitly enabled ptrace
+ if (sys_pipe(fdes) == -1) {
+ // Creating the pipe failed. We'll log an error but carry on anyway,
+ // as we'll probably still get a useful crash report. All that will happen
+ // is the write() and read() calls will fail with EBADF
+ static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump "
+ "sys_pipe failed:";
+ logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
+ logger::write(strerror(errno), strlen(strerror(errno)));
+ logger::write("\n", 1);
+
+ // Ensure fdes[0] and fdes[1] are invalid file descriptors.
+ fdes[0] = fdes[1] = -1;
+ }
+
+ const pid_t child = sys_clone(
+ ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
+ NULL);
+ if (child == -1) {
+ sys_close(fdes[0]);
+ sys_close(fdes[1]);
+ return false;
+ }
+
+ if (child != 0) {
+ static const char clonedMsg[] =
+ "ExceptionHandler::GenerateDump cloned child ";
+ char pidMsg[32] = {};
+
+ unsigned int pidLen = my_uint_len(child);
+ my_uitos(pidMsg, child, pidLen);
+
+ logger::write(clonedMsg, my_strlen(clonedMsg));
+ logger::write(pidMsg, pidLen);
+ logger::write("\n", 1);
+ } else {
+ static const char childMsg[] =
+ "ExceptionHandler::GenerateDump I'm the child\n";
+ logger::write(childMsg, my_strlen(childMsg));
+ }
+
+ // Close the read end of the pipe.
+ sys_close(fdes[0]);
+ // Allow the child to ptrace us
+ sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
+ SendContinueSignalToChild();
+ int status = 0;
+ const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
+
+ sys_close(fdes[1]);
+
+ if (r == -1) {
+ static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
+ logger::write(msg, sizeof(msg) - 1);
+ logger::write(strerror(errno), strlen(strerror(errno)));
+ logger::write("\n", 1);
+ }
+
+ bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
+ if (callback_)
+ success =
+ callback_(minidump_descriptor_, callback_context_, addr_info, success);
+ return success;
+}
+
+// This function runs in a compromised context: see the top of the file.
+void ExceptionHandler::SendContinueSignalToChild() {
+ static const char okToContinueMessage = 'a';
+ int r;
+ r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
+ if (r == -1) {
+ static const char msg[] = "ExceptionHandler::SendContinueSignalToChild "
+ "sys_write failed:";
+ logger::write(msg, sizeof(msg) - 1);
+ logger::write(strerror(errno), strlen(strerror(errno)));
+ logger::write("\n", 1);
+ }
+
+ const char* msg = "ExceptionHandler::SendContinueSignalToChild sent continue signal to child\n";
+ logger::write(msg, my_strlen(msg));
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the cloned process.
+void ExceptionHandler::WaitForContinueSignal() {
+ int r;
+ char receivedMessage;
+
+ const char* waitMsg = "ExceptionHandler::WaitForContinueSignal waiting for continue signal...\n";
+ logger::write(waitMsg, my_strlen(waitMsg));
+
+ r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
+ if (r == -1) {
+ static const char msg[] = "ExceptionHandler::WaitForContinueSignal "
+ "sys_read failed:";
+ logger::write(msg, sizeof(msg) - 1);
+ logger::write(strerror(errno), strlen(strerror(errno)));
+ logger::write("\n", 1);
+ }
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the cloned process.
+bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
+ size_t context_size) {
+ const bool may_skip_dump =
+ minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced();
+ const uintptr_t principal_mapping_address =
+ minidump_descriptor_.address_within_principal_mapping();
+ const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks();
+ if (minidump_descriptor_.IsMicrodumpOnConsole()) {
+ return google_breakpad::WriteMicrodump(
+ crashing_process,
+ context,
+ context_size,
+ mapping_list_,
+ may_skip_dump,
+ principal_mapping_address,
+ sanitize_stacks,
+ *minidump_descriptor_.microdump_extra_info());
+ }
+ if (minidump_descriptor_.IsFD()) {
+ return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
+ minidump_descriptor_.size_limit(),
+ crashing_process,
+ context,
+ context_size,
+ mapping_list_,
+ app_memory_list_,
+ may_skip_dump,
+ principal_mapping_address,
+ sanitize_stacks);
+ }
+ return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
+ minidump_descriptor_.size_limit(),
+ crashing_process,
+ context,
+ context_size,
+ mapping_list_,
+ app_memory_list_,
+ may_skip_dump,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
+ MinidumpDescriptor descriptor(dump_path);
+ ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
+ return eh.WriteMinidump();
+}
+
+// In order to making using EBP to calculate the desired value for ESP
+// a valid operation, ensure that this function is compiled with a
+// frame pointer using the following attribute. This attribute
+// is supported on GCC but not on clang.
+#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__)
+__attribute__((optimize("no-omit-frame-pointer")))
+#endif
+bool ExceptionHandler::WriteMinidump() {
+ if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
+ !minidump_descriptor_.IsMicrodumpOnConsole()) {
+ // Update the path of the minidump so that this can be called multiple times
+ // and new files are created for each minidump. This is done before the
+ // generation happens, as clients may want to access the MinidumpDescriptor
+ // after this call to find the exact path to the minidump file.
+ minidump_descriptor_.UpdatePath();
+ } else if (minidump_descriptor_.IsFD()) {
+ // Reposition the FD to its beginning and resize it to get rid of the
+ // previous minidump info.
+ lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
+ ignore_result(ftruncate(minidump_descriptor_.fd(), 0));
+ }
+
+ // Allow this process to be dumped.
+ sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+
+ CrashContext context;
+ int getcontext_result = getcontext(&context.context);
+ if (getcontext_result)
+ return false;
+
+#if defined(__i386__)
+ // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
+ // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
+ // and it only makes sense when running in kernel mode with a different stack
+ // pointer. When WriteMiniDump is called during normal processing REG_UESP is
+ // zero which leads to bad minidump files.
+ if (!context.context.uc_mcontext.gregs[REG_UESP]) {
+ // If REG_UESP is set to REG_ESP then that includes the stack space for the
+ // CrashContext object in this function, which is about 128 KB. Since the
+ // Linux dumper only records 32 KB of stack this would mean that nothing
+ // useful would be recorded. A better option is to set REG_UESP to REG_EBP,
+ // perhaps with a small negative offset in case there is any code that
+ // objects to them being equal.
+ context.context.uc_mcontext.gregs[REG_UESP] =
+ context.context.uc_mcontext.gregs[REG_EBP] - 16;
+ // The stack saving is based off of REG_ESP so it must be set to match the
+ // new REG_UESP.
+ context.context.uc_mcontext.gregs[REG_ESP] =
+ context.context.uc_mcontext.gregs[REG_UESP];
+ }
+#endif
+
+#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
+ // FPU state is not part of ARM EABI ucontext_t.
+ memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
+ sizeof(context.float_state));
+#endif
+ context.tid = sys_gettid();
+
+ // Add an exception stream to the minidump for better reporting.
+ memset(&context.siginfo, 0, sizeof(context.siginfo));
+ context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
+#if defined(__i386__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
+#elif defined(__x86_64__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
+#elif defined(__arm__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
+#elif defined(__aarch64__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.pc);
+#elif defined(__mips__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.pc);
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+ // nullptr here for phc::AddrInfo* is ok because this is not a crash.
+ return GenerateDump(&context, nullptr);
+}
+
+void ExceptionHandler::AddMappingInfo(const string& name,
+ const wasteful_vector<uint8_t>& identifier,
+ uintptr_t start_address,
+ size_t mapping_size,
+ size_t file_offset) {
+ MappingInfo info;
+ info.start_addr = start_address;
+ info.size = mapping_size;
+ info.offset = file_offset;
+ strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
+ info.name[sizeof(info.name) - 1] = '\0';
+
+ MappingEntry mapping;
+ mapping.first = info;
+ mapping.second.assign(identifier.begin(), identifier.end());
+ mapping_list_.push_back(mapping);
+}
+
+void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
+ if (iter != app_memory_list_.end()) {
+ // Don't allow registering the same pointer twice.
+ return;
+ }
+
+ AppMemory app_memory;
+ app_memory.ptr = ptr;
+ app_memory.length = length;
+ app_memory_list_.push_back(app_memory);
+}
+
+void ExceptionHandler::UnregisterAppMemory(void* ptr) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
+ if (iter != app_memory_list_.end()) {
+ app_memory_list_.erase(iter);
+ }
+}
+
+// static
+bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
+ pid_t child_blamed_thread,
+ const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
+ // This function is not run in a compromised context.
+ MinidumpDescriptor descriptor(dump_path);
+ descriptor.UpdatePath();
+#if defined(MOZ_OXIDIZED_BREAKPAD)
+ nsCString error_msg;
+ if (!write_minidump_linux(descriptor.path(), child, child_blamed_thread, &error_msg))
+ return false;
+#else
+ if (!google_breakpad::WriteMinidump(descriptor.path(),
+ child,
+ child_blamed_thread))
+ return false;
+#endif
+
+ // nullptr here for phc::AddrInfo* is ok because this is not a crash.
+ return callback ? callback(descriptor, callback_context, nullptr, true)
+ : true;
+}
+
+void SetFirstChanceExceptionHandler(FirstChanceHandler callback) {
+ g_first_chance_handler_ = callback;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.h b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.h
new file mode 100644
index 0000000000..46ad399419
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler.h
@@ -0,0 +1,289 @@
+// 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.
+
+#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
+#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ucontext.h>
+
+#include <string>
+
+#include "linux/crash_generation/crash_generation_client.h"
+#include "linux/handler/minidump_descriptor.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/minidump_format.h"
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#else
+namespace mozilla { namespace phc { class AddrInfo {}; } }
+#endif
+
+namespace google_breakpad {
+
+// ExceptionHandler
+//
+// ExceptionHandler can write a minidump file when an exception occurs,
+// or when WriteMinidump() is called explicitly by your program.
+//
+// To have the exception handler write minidumps when an uncaught exception
+// (crash) occurs, you should create an instance early in the execution
+// of your program, and keep it around for the entire time you want to
+// have crash handling active (typically, until shutdown).
+// (NOTE): There should be only be one this kind of exception handler
+// object per process.
+//
+// If you want to write minidumps without installing the exception handler,
+// you can create an ExceptionHandler with install_handler set to false,
+// then call WriteMinidump. You can also use this technique if you want to
+// use different minidump callbacks for different call sites.
+//
+// In either case, a callback function is called when a minidump is written,
+// which receives the full path or file descriptor of the minidump. The
+// caller can collect and write additional application state to that minidump,
+// and launch an external crash-reporting application.
+//
+// Caller should try to make the callbacks as crash-friendly as possible,
+// it should avoid use heap memory allocation as much as possible.
+
+class ExceptionHandler {
+ public:
+ // A callback function to run before Breakpad performs any substantial
+ // processing of an exception. A FilterCallback is called before writing
+ // a minidump. |context| is the parameter supplied by the user as
+ // callback_context when the handler was created.
+ //
+ // If a FilterCallback returns true, Breakpad will continue processing,
+ // attempting to write a minidump. If a FilterCallback returns false,
+ // Breakpad will immediately report the exception as unhandled without
+ // writing a minidump, allowing another handler the opportunity to handle it.
+ typedef bool (*FilterCallback)(void *context);
+
+ // A callback function to run after the minidump has been written.
+ // |descriptor| contains the file descriptor or file path containing the
+ // minidump. |context| is the parameter supplied by the user as
+ // callback_context when the handler was created. |succeeded| indicates
+ // whether a minidump file was successfully written.
+ //
+ // If an exception occurred and the callback returns true, Breakpad will
+ // treat the exception as fully-handled, suppressing any other handlers from
+ // being notified of the exception. If the callback returns false, Breakpad
+ // will treat the exception as unhandled, and allow another handler to handle
+ // it. If there are no other handlers, Breakpad will report the exception to
+ // the system as unhandled, allowing a debugger or native crash dialog the
+ // opportunity to handle the exception. Most callback implementations
+ // should normally return the value of |succeeded|, or when they wish to
+ // not report an exception of handled, false. Callbacks will rarely want to
+ // return true directly (unless |succeeded| is true).
+ typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
+ void* context,
+ const mozilla::phc::AddrInfo* addr_info,
+ bool succeeded);
+
+ // In certain cases, a user may wish to handle the generation of the minidump
+ // themselves. In this case, they can install a handler callback which is
+ // called when a crash has occurred. If this function returns true, no other
+ // processing of occurs and the process will shortly be crashed. If this
+ // returns false, the normal processing continues.
+ typedef bool (*HandlerCallback)(const void* crash_context,
+ size_t crash_context_size,
+ void* context);
+
+ // Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Before writing a minidump, the optional |filter| callback will be called.
+ // Its return value determines whether or not Breakpad should write a
+ // minidump. The minidump content will be written to the file path or file
+ // descriptor from |descriptor|, and the optional |callback| is called after
+ // writing the dump file, as described above.
+ // If install_handler is true, then a minidump will be written whenever
+ // an unhandled exception occurs. If it is false, minidumps will only
+ // be written when WriteMinidump is called.
+ // If |server_fd| is valid, the minidump is generated out-of-process. If it
+ // is -1, in-process generation will always be used.
+ ExceptionHandler(const MinidumpDescriptor& descriptor,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ bool install_handler,
+ const int server_fd);
+ ~ExceptionHandler();
+
+ const MinidumpDescriptor& minidump_descriptor() const {
+ return minidump_descriptor_;
+ }
+
+ void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
+ minidump_descriptor_ = descriptor;
+ }
+
+ void set_crash_handler(HandlerCallback callback) {
+ crash_handler_ = callback;
+ }
+
+ void set_crash_generation_client(CrashGenerationClient* client) {
+ crash_generation_client_.reset(client);
+ }
+
+ // Writes a minidump immediately. This can be used to capture the execution
+ // state independently of a crash.
+ // Returns true on success.
+ // If the ExceptionHandler has been created with a path, a new file is
+ // generated for each minidump. The file path can be retrieved in the
+ // MinidumpDescriptor passed to the MinidumpCallback or by accessing the
+ // MinidumpDescriptor directly from the ExceptionHandler (with
+ // minidump_descriptor()).
+ // If the ExceptionHandler has been created with a file descriptor, the file
+ // descriptor is repositioned to its beginning and the previous generated
+ // minidump is overwritten.
+ // Note that this method is not supposed to be called from a compromised
+ // context as it uses the heap.
+ bool WriteMinidump();
+
+ // Convenience form of WriteMinidump which does not require an
+ // ExceptionHandler instance.
+ static bool WriteMinidump(const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context);
+
+ // Write a minidump of |child| immediately. This can be used to
+ // capture the execution state of |child| independently of a crash.
+ // Pass a meaningful |child_blamed_thread| to make that thread in
+ // the child process the one from which a crash signature is
+ // extracted.
+ //
+ // WARNING: the return of this function *must* happen before
+ // the code that will eventually reap |child| executes.
+ // Otherwise there's a pernicious race condition in which |child|
+ // exits, is reaped, another process created with its pid, then that
+ // new process dumped.
+ static bool WriteMinidumpForChild(pid_t child,
+ pid_t child_blamed_thread,
+ const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context);
+
+ // This structure is passed to minidump_writer.h:WriteMinidump via an opaque
+ // blob. It shouldn't be needed in any user code.
+ struct CrashContext {
+ siginfo_t siginfo;
+ pid_t tid; // the crashing thread.
+ ucontext_t context;
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ // #ifdef this out because FP state is not part of user ABI for Linux ARM.
+ // In case of MIPS Linux FP state is already part of ucontext_t so
+ // 'float_state' is not required.
+ fpstate_t float_state;
+#endif
+ };
+
+ // Returns whether out-of-process dump generation is used or not.
+ bool IsOutOfProcess() const {
+ return crash_generation_client_.get() != NULL;
+ }
+
+ // Add information about a memory mapping. This can be used if
+ // a custom library loader is used that maps things in a way
+ // that the linux dumper can't handle by reading the maps file.
+ void AddMappingInfo(const string& name,
+ const wasteful_vector<uint8_t>& identifier,
+ uintptr_t start_address,
+ size_t mapping_size,
+ size_t file_offset);
+
+ // Register a block of memory of length bytes starting at address ptr
+ // to be copied to the minidump when a crash happens.
+ void RegisterAppMemory(void* ptr, size_t length);
+
+ // Unregister a block of memory that was registered with RegisterAppMemory.
+ void UnregisterAppMemory(void* ptr);
+
+ // Force signal handling for the specified signal.
+ bool SimulateSignalDelivery(int sig);
+
+ // Report a crash signal from an SA_SIGINFO signal handler.
+ bool HandleSignal(int sig, siginfo_t* info, void* uc);
+
+ private:
+ // Save the old signal handlers and install new ones.
+ static bool InstallHandlersLocked();
+ // Restore the old signal handlers.
+ static void RestoreHandlersLocked();
+
+ void PreresolveSymbols();
+ bool GenerateDump(CrashContext *context,
+ const mozilla::phc::AddrInfo* addr_info);
+ void SendContinueSignalToChild();
+ void WaitForContinueSignal();
+
+ static void SignalHandler(int sig, siginfo_t* info, void* uc);
+ static int ThreadEntry(void* arg);
+ bool DoDump(pid_t crashing_process, const void* context,
+ size_t context_size);
+
+ const FilterCallback filter_;
+ const MinidumpCallback callback_;
+ void* const callback_context_;
+
+ scoped_ptr<CrashGenerationClient> crash_generation_client_;
+
+ MinidumpDescriptor minidump_descriptor_;
+
+ // Must be volatile. The compiler is unaware of the code which runs in
+ // the signal handler which reads this variable. Without volatile the
+ // compiler is free to optimise away writes to this variable which it
+ // believes are never read.
+ volatile HandlerCallback crash_handler_;
+
+ // We need to explicitly enable ptrace of parent processes on some
+ // kernels, but we need to know the PID of the cloned process before we
+ // can do this. We create a pipe which we can use to block the
+ // cloned process after creating it, until we have explicitly enabled
+ // ptrace. This is used to store the file descriptors for the pipe
+ int fdes[2] = {-1, -1};
+
+ // Callers can add extra info about mappings for cases where the
+ // dumper code cannot extract enough information from /proc/<pid>/maps.
+ MappingList mapping_list_;
+
+ // Callers can request additional memory regions to be included in
+ // the dump.
+ AppMemoryList app_memory_list_;
+};
+
+typedef bool (*FirstChanceHandler)(int, siginfo_t*, void*);
+void SetFirstChanceExceptionHandler(FirstChanceHandler callback);
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler_unittest.cc
new file mode 100644
index 0000000000..8fa59456c9
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/exception_handler_unittest.cc
@@ -0,0 +1,1290 @@
+// 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 <poll.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#if defined(__mips__)
+#include <sys/cachectl.h>
+#endif
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/ignore_ret.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/using_std_string.h"
+#include "third_party/lss/linux_syscall_support.h"
+#include "google_breakpad/processor/minidump.h"
+
+using namespace google_breakpad;
+
+namespace {
+
+// Flush the instruction cache for a given memory range.
+// Only required on ARM and mips.
+void FlushInstructionCache(const char* memory, uint32_t memory_size) {
+#if defined(__arm__)
+ long begin = reinterpret_cast<long>(memory);
+ long end = begin + static_cast<long>(memory_size);
+# if defined(__ANDROID__)
+ // Provided by Android's <unistd.h>
+ cacheflush(begin, end, 0);
+# elif defined(__linux__)
+ // GLibc/ARM doesn't provide a wrapper for it, do a direct syscall.
+# ifndef __ARM_NR_cacheflush
+# define __ARM_NR_cacheflush 0xf0002
+# endif
+ syscall(__ARM_NR_cacheflush, begin, end, 0);
+# else
+# error "Your operating system is not supported yet"
+# endif
+#elif defined(__mips__)
+# if defined(__ANDROID__)
+ // Provided by Android's <unistd.h>
+ long begin = reinterpret_cast<long>(memory);
+ long end = begin + static_cast<long>(memory_size);
+#if _MIPS_SIM == _ABIO32
+ cacheflush(begin, end, 0);
+#else
+ syscall(__NR_cacheflush, begin, end, ICACHE);
+#endif
+# elif defined(__linux__)
+ // See http://www.linux-mips.org/wiki/Cacheflush_Syscall.
+ cacheflush(const_cast<char*>(memory), memory_size, ICACHE);
+# else
+# error "Your operating system is not supported yet"
+# endif
+#endif
+}
+
+void sigchld_handler(int signo) { }
+
+int CreateTMPFile(const string& dir, string* path) {
+ string file = dir + "/exception-handler-unittest.XXXXXX";
+ const char* c_file = file.c_str();
+ // Copy that string, mkstemp needs a C string it can modify.
+ char* c_path = strdup(c_file);
+ const int fd = mkstemp(c_path);
+ if (fd >= 0)
+ *path = c_path;
+ free(c_path);
+ return fd;
+}
+
+class ExceptionHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sigchld_handler;
+ ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
+ }
+
+ void TearDown() {
+ sigaction(SIGCHLD, &old_action, NULL);
+ }
+
+ struct sigaction old_action;
+};
+
+
+void WaitForProcessToTerminate(pid_t process_id, int expected_status) {
+ int status;
+ ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(expected_status, WTERMSIG(status));
+}
+
+// Reads the minidump path sent over the pipe |fd| and sets it in |path|.
+void ReadMinidumpPathFromPipe(int fd, string* path) {
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(pfd.revents & POLLIN);
+
+ int32_t len;
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(len)), read(fd, &len, sizeof(len)));
+ ASSERT_LT(len, 2048);
+ char* filename = static_cast<char*>(malloc(len + 1));
+ ASSERT_EQ(len, read(fd, filename, len));
+ filename[len] = 0;
+ close(fd);
+ *path = filename;
+ free(filename);
+}
+
+} // namespace
+
+TEST(ExceptionHandlerTest, SimpleWithPath) {
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+ EXPECT_EQ(temp_dir.path(), handler.minidump_descriptor().directory());
+ string temp_subdir = temp_dir.path() + "/subdir";
+ handler.set_minidump_descriptor(MinidumpDescriptor(temp_subdir));
+ EXPECT_EQ(temp_subdir, handler.minidump_descriptor().directory());
+}
+
+TEST(ExceptionHandlerTest, SimpleWithFD) {
+ AutoTempDir temp_dir;
+ string path;
+ const int fd = CreateTMPFile(temp_dir.path(), &path);
+ ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1);
+ close(fd);
+}
+
+static bool DoneCallback(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ if (!succeeded)
+ return false;
+
+ if (!descriptor.IsFD()) {
+ int fd = reinterpret_cast<intptr_t>(context);
+ uint32_t len = 0;
+ len = my_strlen(descriptor.path());
+ IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len))));
+ IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len)));
+ }
+ return true;
+}
+
+#ifndef ADDRESS_SANITIZER
+
+// This is a replacement for "*reinterpret_cast<volatile int*>(NULL) = 0;"
+// It is needed because GCC is allowed to assume that the program will
+// not execute any undefined behavior (UB) operation. Further, when GCC
+// observes that UB statement is reached, it can assume that all statements
+// leading to the UB one are never executed either, and can completely
+// optimize them out. In the case of ExceptionHandlerTest::ExternalDumper,
+// GCC-4.9 optimized out the entire set up of ExceptionHandler, causing
+// test failure.
+volatile int *p_null; // external linkage, so GCC can't tell that it
+ // remains NULL. Volatile just for a good measure.
+static void DoNullPointerDereference() {
+ *p_null = 1;
+}
+
+void ChildCrash(bool use_fd) {
+ AutoTempDir temp_dir;
+ int fds[2] = {0};
+ int minidump_fd = -1;
+ string minidump_path;
+ if (use_fd) {
+ minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path);
+ } else {
+ ASSERT_NE(pipe(fds), -1);
+ }
+
+ const pid_t child = fork();
+ if (child == 0) {
+ {
+ google_breakpad::scoped_ptr<ExceptionHandler> handler;
+ if (use_fd) {
+ handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd),
+ NULL, NULL, NULL, true, -1));
+ } else {
+ close(fds[0]); // Close the reading end.
+ void* fd_param = reinterpret_cast<void*>(fds[1]);
+ handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()),
+ NULL, DoneCallback, fd_param,
+ true, -1));
+ }
+ // Crash with the exception handler in scope.
+ DoNullPointerDereference();
+ }
+ }
+ if (!use_fd)
+ close(fds[1]); // Close the writting end.
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+
+ if (!use_fd)
+ ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+ unlink(minidump_path.c_str());
+}
+
+TEST(ExceptionHandlerTest, ChildCrashWithPath) {
+ ASSERT_NO_FATAL_FAILURE(ChildCrash(false));
+}
+
+TEST(ExceptionHandlerTest, ChildCrashWithFD) {
+ ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
+}
+
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
+static void* SleepFunction(void* unused) {
+ while (true) usleep(1000000);
+ return NULL;
+}
+
+static void* CrashFunction(void* b_ptr) {
+ pthread_barrier_t* b = reinterpret_cast<pthread_barrier_t*>(b_ptr);
+ pthread_barrier_wait(b);
+ DoNullPointerDereference();
+ return NULL;
+}
+
+// Tests that concurrent crashes do not enter a loop by alternately triggering
+// the signal handler.
+TEST(ExceptionHandlerTest, ParallelChildCrashesDontHang) {
+ AutoTempDir temp_dir;
+ const pid_t child = fork();
+ if (child == 0) {
+ google_breakpad::scoped_ptr<ExceptionHandler> handler(
+ new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
+ NULL, true, -1));
+
+ // We start a number of threads to make sure handling the signal takes
+ // enough time for the second thread to enter the signal handler.
+ int num_sleep_threads = 100;
+ google_breakpad::scoped_array<pthread_t> sleep_threads(
+ new pthread_t[num_sleep_threads]);
+ for (int i = 0; i < num_sleep_threads; ++i) {
+ ASSERT_EQ(0, pthread_create(&sleep_threads[i], NULL, SleepFunction,
+ NULL));
+ }
+
+ int num_crash_threads = 2;
+ google_breakpad::scoped_array<pthread_t> crash_threads(
+ new pthread_t[num_crash_threads]);
+ // Barrier to synchronize crashing both threads at the same time.
+ pthread_barrier_t b;
+ ASSERT_EQ(0, pthread_barrier_init(&b, NULL, num_crash_threads + 1));
+ for (int i = 0; i < num_crash_threads; ++i) {
+ ASSERT_EQ(0, pthread_create(&crash_threads[i], NULL, CrashFunction, &b));
+ }
+ pthread_barrier_wait(&b);
+ for (int i = 0; i < num_crash_threads; ++i) {
+ ASSERT_EQ(0, pthread_join(crash_threads[i], NULL));
+ }
+ }
+
+ // Wait a while until the child should have crashed.
+ usleep(1000000);
+ // Kill the child if it is still running.
+ kill(child, SIGKILL);
+
+ // If the child process terminated by itself, it will have returned SIGSEGV.
+ // If however it got stuck in a loop, it will have been killed by the
+ // SIGKILL.
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+}
+#endif // !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
+
+static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ return false;
+}
+
+static bool DoneCallbackReturnTrue(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ return true;
+}
+
+static bool DoneCallbackRaiseSIGKILL(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ raise(SIGKILL);
+ return true;
+}
+
+static bool FilterCallbackReturnFalse(void* context) {
+ return false;
+}
+
+static bool FilterCallbackReturnTrue(void* context) {
+ return true;
+}
+
+// SIGKILL cannot be blocked and a handler cannot be installed for it. In the
+// following tests, if the child dies with signal SIGKILL, then the signal was
+// redelivered to this handler. If the child dies with SIGSEGV then it wasn't.
+static void RaiseSIGKILL(int sig) {
+ raise(SIGKILL);
+}
+
+static bool InstallRaiseSIGKILL() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = RaiseSIGKILL;
+ return sigaction(SIGSEGV, &sa, NULL) != -1;
+}
+
+static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
+ ExceptionHandler::MinidumpCallback done,
+ string path) {
+ ExceptionHandler handler(
+ MinidumpDescriptor(path), filter, done, NULL, true, -1);
+ // Crash with the exception handler in scope.
+ DoNullPointerDereference();
+}
+
+TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+ CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+TEST(ExceptionHandlerTest, RedeliveryOnDoneCallbackFalse) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+ CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+TEST(ExceptionHandlerTest, NoRedeliveryOnDoneCallbackTrue) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+ CrashWithCallbacks(NULL, DoneCallbackReturnTrue, temp_dir.path());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+}
+
+TEST(ExceptionHandlerTest, NoRedeliveryOnFilterCallbackTrue) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+ CrashWithCallbacks(FilterCallbackReturnTrue, NULL, temp_dir.path());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+}
+
+TEST(ExceptionHandlerTest, RedeliveryToDefaultHandler) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ // Custom signal handlers, which may have been installed by a test launcher,
+ // are undesirable in this child.
+ signal(SIGSEGV, SIG_DFL);
+
+ CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
+ }
+
+ // As RaiseSIGKILL wasn't installed, the redelivery should just kill the child
+ // with SIGSEGV.
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+}
+
+// Check that saving and restoring the signal handler with 'signal'
+// instead of 'sigaction' doesn't make the Breakpad signal handler
+// crash. See comments in ExceptionHandler::SignalHandler for full
+// details.
+TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) {
+ AutoTempDir temp_dir;
+ const pid_t child = fork();
+ if (child == 0) {
+ // Install the RaiseSIGKILL handler for SIGSEGV.
+ ASSERT_TRUE(InstallRaiseSIGKILL());
+
+ // Create a new exception handler, this installs a new SIGSEGV
+ // handler, after saving the old one.
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallbackReturnFalse, NULL, true, -1);
+
+ // Install the default SIGSEGV handler, saving the current one.
+ // Then re-install the current one with 'signal', this loses the
+ // SA_SIGINFO flag associated with the Breakpad handler.
+ sighandler_t old_handler = signal(SIGSEGV, SIG_DFL);
+ ASSERT_NE(reinterpret_cast<void*>(old_handler),
+ reinterpret_cast<void*>(SIG_ERR));
+ ASSERT_NE(reinterpret_cast<void*>(signal(SIGSEGV, old_handler)),
+ reinterpret_cast<void*>(SIG_ERR));
+
+ // Crash with the exception handler in scope.
+ DoNullPointerDereference();
+ }
+ // SIGKILL means Breakpad's signal handler didn't crash.
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+TEST(ExceptionHandlerTest, StackedHandlersDeliveredToTop) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
+ NULL,
+ NULL,
+ NULL,
+ true,
+ -1);
+ CrashWithCallbacks(NULL, DoneCallbackRaiseSIGKILL, temp_dir.path());
+ }
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+TEST(ExceptionHandlerTest, StackedHandlersNotDeliveredToBottom) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
+ NULL,
+ DoneCallbackRaiseSIGKILL,
+ NULL,
+ true,
+ -1);
+ CrashWithCallbacks(NULL, NULL, temp_dir.path());
+ }
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+}
+
+TEST(ExceptionHandlerTest, StackedHandlersFilteredToBottom) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
+ NULL,
+ DoneCallbackRaiseSIGKILL,
+ NULL,
+ true,
+ -1);
+ CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
+ }
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
+ NULL,
+ DoneCallbackRaiseSIGKILL,
+ NULL,
+ true,
+ -1);
+ CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path());
+ }
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
+}
+
+namespace {
+const int kSimpleFirstChanceReturnStatus = 42;
+bool SimpleFirstChanceHandler(int, siginfo_t*, void*) {
+ _exit(kSimpleFirstChanceReturnStatus);
+}
+}
+
+TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) {
+ AutoTempDir temp_dir;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+ google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler);
+ DoNullPointerDereference();
+ }
+ int status;
+ ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status));
+}
+
+#endif // !ADDRESS_SANITIZER
+
+const unsigned char kIllegalInstruction[] = {
+#if defined(__mips__)
+ // mfc2 zero,Impl - usually illegal in userspace.
+ 0x48, 0x00, 0x00, 0x48
+#else
+ // This crashes with SIGILL on x86/x86-64/arm.
+ 0xff, 0xff, 0xff, 0xff
+#endif
+};
+
+// Test that memory around the instruction pointer is written
+// to the dump as a MinidumpMemoryRegion.
+TEST(ExceptionHandlerTest, InstructionPointerMemory) {
+ AutoTempDir temp_dir;
+ int fds[2];
+ ASSERT_NE(pipe(fds), -1);
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = 256; // bytes
+ const int kOffset = kMemorySize / 2;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallback, reinterpret_cast<void*>(fds[1]),
+ true, -1);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them in the middle
+ // of the block of memory, because the minidump should contain 128
+ // bytes on either side of the instruction pointer.
+ memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
+ FlushInstructionCache(memory, kMemorySize);
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ }
+ close(fds[1]);
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
+
+ string minidump_path;
+ ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT(0U, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t prefix_bytes[kOffset];
+ uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
+ sizeof(kIllegalInstruction)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+
+ unlink(minidump_path.c_str());
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the low end.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
+ AutoTempDir temp_dir;
+ int fds[2];
+ ASSERT_NE(pipe(fds), -1);
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = 256; // bytes
+ const int kOffset = 0;
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallback, reinterpret_cast<void*>(fds[1]),
+ true, -1);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them in the middle
+ // of the block of memory, because the minidump should contain 128
+ // bytes on either side of the instruction pointer.
+ memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
+ FlushInstructionCache(memory, kMemorySize);
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ }
+ close(fds[1]);
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
+
+ string minidump_path;
+ ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT(0U, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize / 2, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)];
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
+ sizeof(kIllegalInstruction)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+ unlink(minidump_path.c_str());
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the high end.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
+ AutoTempDir temp_dir;
+ int fds[2];
+ ASSERT_NE(pipe(fds), -1);
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ // Use 4k here because the OS will hand out a single page even
+ // if a smaller size is requested, and this test wants to
+ // test the upper bound of the memory range.
+ const uint32_t kMemorySize = 4096; // bytes
+ const int kOffset = kMemorySize - sizeof(kIllegalInstruction);
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallback, reinterpret_cast<void*>(fds[1]),
+ true, -1);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them in the middle
+ // of the block of memory, because the minidump should contain 128
+ // bytes on either side of the instruction pointer.
+ memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
+ FlushInstructionCache(memory, kMemorySize);
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ }
+ close(fds[1]);
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
+
+ string minidump_path;
+ ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ // Read the minidump. Locate the exception record and the memory list, and
+ // then ensure that there is a memory region in the memory list that covers
+ // the instruction pointer from the exception record.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT(0U, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ const size_t kPrefixSize = 128; // bytes
+ EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t prefix_bytes[kPrefixSize];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kPrefixSize,
+ kIllegalInstruction, sizeof(kIllegalInstruction)) == 0);
+
+ unlink(minidump_path.c_str());
+}
+
+#ifndef ADDRESS_SANITIZER
+
+// Ensure that an extra memory block doesn't get added when the instruction
+// pointer is not in mapped memory.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
+ AutoTempDir temp_dir;
+ int fds[2];
+ ASSERT_NE(pipe(fds), -1);
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
+ DoneCallback, reinterpret_cast<void*>(fds[1]),
+ true, -1);
+ // Try calling a NULL pointer.
+ typedef void (*void_function)(void);
+ // Volatile markings are needed to keep Clang from generating invalid
+ // opcodes. See http://crbug.com/498354 for details.
+ volatile void_function memory_function =
+ reinterpret_cast<void_function>(NULL);
+ memory_function();
+ // not reached
+ exit(1);
+ }
+ close(fds[1]);
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+
+ string minidump_path;
+ ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is no memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+
+ MinidumpContext* exception_context = exception->GetContext();
+ ASSERT_TRUE(exception_context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(exception_context->GetInstructionPointer(&instruction_pointer));
+ EXPECT_EQ(instruction_pointer, 0u);
+
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(memory_list);
+
+ unsigned int region_count = memory_list->region_count();
+ ASSERT_GE(region_count, 1u);
+
+ for (unsigned int region_index = 0;
+ region_index < region_count;
+ ++region_index) {
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionAtIndex(region_index);
+ uint64_t region_base = region->GetBase();
+ EXPECT_FALSE(instruction_pointer >= region_base &&
+ instruction_pointer < region_base + region->GetSize());
+ }
+
+ unlink(minidump_path.c_str());
+}
+
+#endif // !ADDRESS_SANITIZER
+
+// Test that anonymous memory maps can be annotated with names and IDs.
+TEST(ExceptionHandlerTest, ModuleInfo) {
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
+ const char* kMemoryName = "a fake module";
+ const uint8_t kModuleGUID[sizeof(MDGUID)] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+ };
+ const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
+
+ // Get some memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ PageAllocator allocator;
+ auto_wasteful_vector<uint8_t, sizeof(MDGUID)> guid(&allocator);
+ guid.assign(std::begin(kModuleGUID), std::end(kModuleGUID));
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+
+ // Add info about the anonymous memory mapping.
+ handler.AddMappingInfo(kMemoryName,
+ guid,
+ kMemoryAddress,
+ kMemorySize,
+ 0);
+ ASSERT_TRUE(handler.WriteMinidump());
+
+ const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
+ // Read the minidump. Load the module list, and ensure that the mmap'ed
+ // |memory| is listed with the given module name and debug ID.
+ Minidump minidump(minidump_desc.path());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* module =
+ module_list->GetModuleForAddress(kMemoryAddress);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(kMemoryAddress, module->base_address());
+ EXPECT_EQ(kMemorySize, module->size());
+ EXPECT_EQ(kMemoryName, module->code_file());
+ EXPECT_EQ(module_identifier, module->debug_identifier());
+
+ unlink(minidump_desc.path());
+}
+
+#ifndef ADDRESS_SANITIZER
+
+static const unsigned kControlMsgSize =
+ CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+
+static bool
+CrashHandler(const void* crash_context, size_t crash_context_size,
+ void* context) {
+ const int fd = (intptr_t) context;
+ int fds[2];
+ if (pipe(fds) == -1) {
+ // There doesn't seem to be any way to reliably handle
+ // this failure without the parent process hanging
+ // At least make sure that this process doesn't access
+ // unexpected file descriptors
+ fds[0] = -1;
+ fds[1] = -1;
+ }
+ struct kernel_msghdr msg = {0};
+ struct kernel_iovec iov;
+ iov.iov_base = const_cast<void*>(crash_context);
+ iov.iov_len = crash_context_size;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ char cmsg[kControlMsgSize];
+ memset(cmsg, 0, kControlMsgSize);
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_RIGHTS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(int));
+ *((int*) CMSG_DATA(hdr)) = fds[1];
+ hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_CREDENTIALS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ cred->uid = getuid();
+ cred->gid = getgid();
+ cred->pid = getpid();
+
+ ssize_t ret = HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
+ sys_close(fds[1]);
+ if (ret <= 0)
+ return false;
+
+ char b;
+ IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
+
+ return true;
+}
+
+TEST(ExceptionHandlerTest, ExternalDumper) {
+ int fds[2];
+ ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
+ static const int on = 1;
+ setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL,
+ reinterpret_cast<void*>(fds[1]), true, -1);
+ handler.set_crash_handler(CrashHandler);
+ DoNullPointerDereference();
+ }
+ close(fds[1]);
+ struct msghdr msg = {0};
+ struct iovec iov;
+ static const unsigned kCrashContextSize =
+ sizeof(ExceptionHandler::CrashContext);
+ char context[kCrashContextSize];
+ char control[kControlMsgSize];
+ iov.iov_base = context;
+ iov.iov_len = kCrashContextSize;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = kControlMsgSize;
+
+ const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
+ ASSERT_EQ(static_cast<ssize_t>(kCrashContextSize), n);
+ ASSERT_EQ(kControlMsgSize, msg.msg_controllen);
+ ASSERT_EQ(static_cast<__typeof__(msg.msg_flags)>(0), msg.msg_flags);
+ ASSERT_EQ(0, close(fds[0]));
+
+ pid_t crashing_pid = -1;
+ int signal_fd = -1;
+ for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+ hdr = CMSG_NXTHDR(&msg, hdr)) {
+ if (hdr->cmsg_level != SOL_SOCKET)
+ continue;
+ if (hdr->cmsg_type == SCM_RIGHTS) {
+ const unsigned len = hdr->cmsg_len -
+ (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+ ASSERT_EQ(sizeof(int), len);
+ signal_fd = *(reinterpret_cast<int*>(CMSG_DATA(hdr)));
+ } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+ const struct ucred *cred =
+ reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ crashing_pid = cred->pid;
+ }
+ }
+
+ ASSERT_NE(crashing_pid, -1);
+ ASSERT_NE(signal_fd, -1);
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + "/exception-handler-unittest";
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context,
+ kCrashContextSize));
+ static const char b = 0;
+ ASSERT_EQ(1, (HANDLE_EINTR(write(signal_fd, &b, 1))));
+ ASSERT_EQ(0, close(signal_fd));
+
+ ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
+
+ struct stat st;
+ ASSERT_EQ(0, stat(templ.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+ unlink(templ.c_str());
+}
+
+#endif // !ADDRESS_SANITIZER
+
+TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) {
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
+ NULL, false, -1);
+ ASSERT_TRUE(handler.WriteMinidump());
+
+ string minidump_path = handler.minidump_descriptor().path();
+
+ // Read the minidump and check the exception stream.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+ const MDRawExceptionStream* raw = exception->exception();
+ ASSERT_TRUE(raw);
+ EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
+ raw->exception_record.exception_code);
+}
+
+TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) {
+ AutoTempDir temp_dir;
+ string path;
+ const int fd = CreateTMPFile(temp_dir.path(), &path);
+ ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1);
+ ASSERT_TRUE(handler.WriteMinidump());
+ // Check by the size of the data written to the FD that a minidump was
+ // generated.
+ off_t size = lseek(fd, 0, SEEK_CUR);
+ ASSERT_GT(size, 0);
+
+ // Generate another minidump.
+ ASSERT_TRUE(handler.WriteMinidump());
+ size = lseek(fd, 0, SEEK_CUR);
+ ASSERT_GT(size, 0);
+}
+
+TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) {
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
+ NULL, false, -1);
+ ASSERT_TRUE(handler.WriteMinidump());
+
+ const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor();
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_1.path(), &st));
+ ASSERT_GT(st.st_size, 0);
+ string minidump_1_path(minidump_1.path());
+ // Check it is a valid minidump.
+ Minidump minidump1(minidump_1_path);
+ ASSERT_TRUE(minidump1.Read());
+ unlink(minidump_1.path());
+
+ // Generate another minidump, it should go to a different file.
+ ASSERT_TRUE(handler.WriteMinidump());
+ const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor();
+ ASSERT_EQ(0, stat(minidump_2.path(), &st));
+ ASSERT_GT(st.st_size, 0);
+ string minidump_2_path(minidump_2.path());
+ // Check it is a valid minidump.
+ Minidump minidump2(minidump_2_path);
+ ASSERT_TRUE(minidump2.Read());
+ unlink(minidump_2.path());
+
+ // 2 distinct files should be produced.
+ ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str());
+}
+
+// Test that an additional memory region can be added to the minidump.
+TEST(ExceptionHandlerTest, AdditionalMemory) {
+ const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ uint8_t* memory = new uint8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (uint32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+ handler.WriteMinidump();
+
+ const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(minidump_desc.path());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemoryAddress, region->GetBase());
+ EXPECT_EQ(kMemorySize, region->GetSize());
+
+ // Verify memory contents.
+ EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
+
+ delete[] memory;
+}
+
+// Test that a memory region that was previously registered
+// can be unregistered.
+TEST(ExceptionHandlerTest, AdditionalMemoryRemove) {
+ const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ uint8_t* memory = new uint8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+
+ // ...and then remove it
+ handler.UnregisterAppMemory(memory);
+ handler.WriteMinidump();
+
+ const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
+
+ // Read the minidump. Ensure that the memory region is not present.
+ Minidump minidump(minidump_desc.path());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ EXPECT_FALSE(region);
+
+ delete[] memory;
+}
+
+static bool SimpleCallback(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ string* filename = reinterpret_cast<string*>(context);
+ *filename = descriptor.path();
+ return true;
+}
+
+TEST(ExceptionHandlerTest, WriteMinidumpForChild) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ AutoTempDir temp_dir;
+ string minidump_filename;
+ ASSERT_TRUE(
+ ExceptionHandler::WriteMinidumpForChild(child, child,
+ temp_dir.path(), SimpleCallback,
+ (void*)&minidump_filename));
+
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+ // Check that the crashing thread is the main thread of |child|
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+ uint32_t thread_id;
+ ASSERT_TRUE(exception->GetThreadID(&thread_id));
+ EXPECT_EQ(child, static_cast<int32_t>(thread_id));
+
+ const MDRawExceptionStream* raw = exception->exception();
+ ASSERT_TRUE(raw);
+ EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
+ raw->exception_record.exception_code);
+
+ close(fds[1]);
+ unlink(minidump_filename.c_str());
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.cc b/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.cc
new file mode 100644
index 0000000000..b0ef05bd9d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.cc
@@ -0,0 +1,108 @@
+// 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 "linux/handler/guid_generator.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+//
+// GUIDGenerator
+//
+// This class is used to generate random GUID.
+// Currently use random number to generate a GUID since Linux has
+// no native GUID generator. This should be OK since we don't expect
+// crash to happen very offen.
+//
+class GUIDGenerator {
+ public:
+ static uint16_t BytesToUInt16(const uint8_t bytes[]) {
+ return ((uint16_t) bytes[1] << 8) | ((uint16_t) bytes[0]);
+ }
+
+ // The last field in a GUID is 48 bits long so we're converting only 6 bytes
+ static uint64_t BytesToUInt48(const uint8_t bytes[]) {
+ return ((uint64_t) bytes[0] << 40) | ((uint64_t) bytes[1] << 32) |
+ ((uint64_t) bytes[2] << 24) | ((uint64_t) bytes[3] << 16) |
+ ((uint64_t) bytes[4] << 8) | (uint64_t) bytes[5];
+ }
+
+ static void UInt32ToBytes(uint8_t bytes[], uint32_t n) {
+ bytes[0] = n & 0xff;
+ bytes[1] = (n >> 8) & 0xff;
+ bytes[2] = (n >> 16) & 0xff;
+ bytes[3] = (n >> 24) & 0xff;
+ }
+
+ static bool CreateGUID(GUID *guid) {
+ InitOnce();
+ guid->data1 = random();
+ guid->data2 = (uint16_t)(random());
+ guid->data3 = (uint16_t)(random());
+ UInt32ToBytes(&guid->data4[0], random());
+ UInt32ToBytes(&guid->data4[4], random());
+ return true;
+ }
+
+ private:
+ static void InitOnce() {
+ pthread_once(&once_control, &InitOnceImpl);
+ }
+
+ static void InitOnceImpl() {
+ srandom(time(NULL));
+ }
+
+ static pthread_once_t once_control;
+};
+
+pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
+
+bool CreateGUID(GUID *guid) {
+ return GUIDGenerator::CreateGUID(guid);
+}
+
+// Parse guid to string.
+bool GUIDToString(const GUID *guid, char *buf, size_t buf_len) {
+ // Should allow more space the the max length of GUID.
+ assert(buf_len > kGUIDStringLength);
+ int num = snprintf(buf, buf_len, kGUIDFormatString,
+ guid->data1, guid->data2, guid->data3,
+ GUIDGenerator::BytesToUInt16(&(guid->data4[0])),
+ GUIDGenerator::BytesToUInt48(&(guid->data4[2])));
+ if (num != kGUIDStringLength)
+ return false;
+
+ buf[num] = '\0';
+ return true;
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.h b/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.h
new file mode 100644
index 0000000000..de97eda1cb
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/guid_generator.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef LINUX_HANDLER_GUID_GENERATOR_H__
+#define LINUX_HANDLER_GUID_GENERATOR_H__
+
+#include "google_breakpad/common/minidump_format.h"
+
+typedef MDGUID GUID;
+
+// Format string for parsing GUID.
+const char kGUIDFormatString[] = "%08x-%04x-%04x-%04x-%012" PRIx64;
+// Length of GUID string. Don't count the ending '\0'.
+const size_t kGUIDStringLength = 36;
+
+// Create a guid.
+bool CreateGUID(GUID *guid);
+
+// Get the string from guid.
+bool GUIDToString(const GUID *guid, char *buf, size_t buf_len);
+
+#endif
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/microdump_extra_info.h b/toolkit/crashreporter/breakpad-client/linux/handler/microdump_extra_info.h
new file mode 100644
index 0000000000..bf01f0c7b1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/microdump_extra_info.h
@@ -0,0 +1,52 @@
+// Copyright 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.
+
+#ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
+#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
+
+namespace google_breakpad {
+
+struct MicrodumpExtraInfo {
+ // Strings pointed to by this struct are not copied, and are
+ // expected to remain valid for the lifetime of the process.
+ const char* build_fingerprint;
+ const char* product_info;
+ const char* gpu_fingerprint;
+ const char* process_type;
+
+ MicrodumpExtraInfo()
+ : build_fingerprint(NULL),
+ product_info(NULL),
+ gpu_fingerprint(NULL),
+ process_type(NULL) {}
+};
+
+}
+
+#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.cc b/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.cc
new file mode 100644
index 0000000000..21cf4b0dce
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.cc
@@ -0,0 +1,96 @@
+// 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.
+
+#include <stdio.h>
+
+#include "linux/handler/guid_generator.h"
+#include "linux/handler/minidump_descriptor.h"
+
+namespace google_breakpad {
+
+//static
+const MinidumpDescriptor::MicrodumpOnConsole
+ MinidumpDescriptor::kMicrodumpOnConsole = {};
+
+MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
+ : mode_(descriptor.mode_),
+ fd_(descriptor.fd_),
+ directory_(descriptor.directory_),
+ c_path_(NULL),
+ size_limit_(descriptor.size_limit_),
+ address_within_principal_mapping_(
+ descriptor.address_within_principal_mapping_),
+ skip_dump_if_principal_mapping_not_referenced_(
+ descriptor.skip_dump_if_principal_mapping_not_referenced_),
+ sanitize_stacks_(descriptor.sanitize_stacks_),
+ microdump_extra_info_(descriptor.microdump_extra_info_) {
+ // The copy constructor is not allowed to be called on a MinidumpDescriptor
+ // with a valid path_, as getting its c_path_ would require the heap which
+ // can cause problems in compromised environments.
+ assert(descriptor.path_.empty());
+}
+
+MinidumpDescriptor& MinidumpDescriptor::operator=(
+ const MinidumpDescriptor& descriptor) {
+ assert(descriptor.path_.empty());
+
+ mode_ = descriptor.mode_;
+ fd_ = descriptor.fd_;
+ directory_ = descriptor.directory_;
+ path_.clear();
+ if (c_path_) {
+ // This descriptor already had a path set, so generate a new one.
+ c_path_ = NULL;
+ UpdatePath();
+ }
+ size_limit_ = descriptor.size_limit_;
+ address_within_principal_mapping_ =
+ descriptor.address_within_principal_mapping_;
+ skip_dump_if_principal_mapping_not_referenced_ =
+ descriptor.skip_dump_if_principal_mapping_not_referenced_;
+ sanitize_stacks_ = descriptor.sanitize_stacks_;
+ microdump_extra_info_ = descriptor.microdump_extra_info_;
+ return *this;
+}
+
+void MinidumpDescriptor::UpdatePath() {
+ assert(mode_ == kWriteMinidumpToFile && !directory_.empty());
+
+ GUID guid;
+ char guid_str[kGUIDStringLength + 1];
+ if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
+ assert(false);
+ }
+
+ path_.clear();
+ path_ = directory_ + "/" + guid_str + ".dmp";
+ c_path_ = path_.c_str();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.h b/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.h
new file mode 100644
index 0000000000..c3deae8a74
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/handler/minidump_descriptor.h
@@ -0,0 +1,199 @@
+// 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.
+
+#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
+#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
+
+#include <assert.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "linux/handler/microdump_extra_info.h"
+#include "common/using_std_string.h"
+
+// This class describes how a crash dump should be generated, either:
+// - Writing a full minidump to a file in a given directory (the actual path,
+// inside the directory, is determined by this class).
+// - Writing a full minidump to a given fd.
+// - Writing a reduced microdump to the console (logcat on Android).
+namespace google_breakpad {
+
+class MinidumpDescriptor {
+ public:
+ struct MicrodumpOnConsole {};
+ static const MicrodumpOnConsole kMicrodumpOnConsole;
+
+ MinidumpDescriptor()
+ : mode_(kUninitialized),
+ fd_(-1),
+ size_limit_(-1),
+ address_within_principal_mapping_(0),
+ skip_dump_if_principal_mapping_not_referenced_(false) {}
+
+ explicit MinidumpDescriptor(const string& directory)
+ : mode_(kWriteMinidumpToFile),
+ fd_(-1),
+ directory_(directory),
+ c_path_(NULL),
+ size_limit_(-1),
+ address_within_principal_mapping_(0),
+ skip_dump_if_principal_mapping_not_referenced_(false),
+ sanitize_stacks_(false) {
+ assert(!directory.empty());
+ }
+
+ explicit MinidumpDescriptor(int fd)
+ : mode_(kWriteMinidumpToFd),
+ fd_(fd),
+ c_path_(NULL),
+ size_limit_(-1),
+ address_within_principal_mapping_(0),
+ skip_dump_if_principal_mapping_not_referenced_(false),
+ sanitize_stacks_(false) {
+ assert(fd != -1);
+ }
+
+ explicit MinidumpDescriptor(const MicrodumpOnConsole&)
+ : mode_(kWriteMicrodumpToConsole),
+ fd_(-1),
+ size_limit_(-1),
+ address_within_principal_mapping_(0),
+ skip_dump_if_principal_mapping_not_referenced_(false),
+ sanitize_stacks_(false) {}
+
+ explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
+ MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
+
+ static MinidumpDescriptor getMicrodumpDescriptor();
+
+ bool IsFD() const { return mode_ == kWriteMinidumpToFd; }
+
+ int fd() const { return fd_; }
+
+ string directory() const { return directory_; }
+
+ const char* path() const { return c_path_; }
+
+ bool IsMicrodumpOnConsole() const {
+ return mode_ == kWriteMicrodumpToConsole;
+ }
+
+ // Updates the path so it is unique.
+ // Should be called from a normal context: this methods uses the heap.
+ void UpdatePath();
+
+ off_t size_limit() const { return size_limit_; }
+ void set_size_limit(off_t limit) { size_limit_ = limit; }
+
+ uintptr_t address_within_principal_mapping() const {
+ return address_within_principal_mapping_;
+ }
+ void set_address_within_principal_mapping(
+ uintptr_t address_within_principal_mapping) {
+ address_within_principal_mapping_ = address_within_principal_mapping;
+ }
+
+ bool skip_dump_if_principal_mapping_not_referenced() {
+ return skip_dump_if_principal_mapping_not_referenced_;
+ }
+ void set_skip_dump_if_principal_mapping_not_referenced(
+ bool skip_dump_if_principal_mapping_not_referenced) {
+ skip_dump_if_principal_mapping_not_referenced_ =
+ skip_dump_if_principal_mapping_not_referenced;
+ }
+
+ bool sanitize_stacks() const { return sanitize_stacks_; }
+ void set_sanitize_stacks(bool sanitize_stacks) {
+ sanitize_stacks_ = sanitize_stacks;
+ }
+
+ MicrodumpExtraInfo* microdump_extra_info() {
+ assert(IsMicrodumpOnConsole());
+ return &microdump_extra_info_;
+ }
+
+ private:
+ enum DumpMode {
+ kUninitialized = 0,
+ kWriteMinidumpToFile,
+ kWriteMinidumpToFd,
+ kWriteMicrodumpToConsole
+ };
+
+ // Specifies the dump mode (see DumpMode).
+ DumpMode mode_;
+
+ // The file descriptor where the minidump is generated.
+ int fd_;
+
+ // The directory where the minidump should be generated.
+ string directory_;
+
+ // The full path to the generated minidump.
+ string path_;
+
+ // The C string of |path_|. Precomputed so it can be access from a compromised
+ // context.
+ const char* c_path_;
+
+ off_t size_limit_;
+
+ // This member points somewhere into the main module for this
+ // process (the module that is considerered interesting for the
+ // purposes of debugging crashes).
+ uintptr_t address_within_principal_mapping_;
+
+ // If set, threads that do not reference the address range
+ // associated with |address_within_principal_mapping_| will not have their
+ // stacks logged.
+ bool skip_dump_if_principal_mapping_not_referenced_;
+
+ // If set, stacks are sanitized to remove PII. This involves
+ // overwriting any pointer-aligned words that are not either
+ // pointers into a process mapping or small integers (+/-4096). This
+ // leaves enough information to unwind stacks, and preserve some
+ // register values, but elides strings and other program data.
+ bool sanitize_stacks_;
+
+ // The extra microdump data (e.g. product name/version, build
+ // fingerprint, gpu fingerprint) that should be appended to the dump
+ // (microdump only). Microdumps don't have the ability of appending
+ // extra metadata after the dump is generated (as opposite to
+ // minidumps MIME fields), therefore the extra data must be provided
+ // upfront. Any memory pointed to by members of the
+ // MicrodumpExtraInfo struct must be valid for the lifetime of the
+ // process (read: the caller has to guarantee that it is stored in
+ // global static storage.)
+ MicrodumpExtraInfo microdump_extra_info_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/log/log.cc b/toolkit/crashreporter/breakpad-client/linux/log/log.cc
new file mode 100644
index 0000000000..3346acc6d8
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/log/log.cc
@@ -0,0 +1,84 @@
+// 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.
+
+#include "linux/log/log.h"
+
+#if defined(__ANDROID__)
+#include <android/log.h>
+#include <dlfcn.h>
+#else
+#include "third_party/lss/linux_syscall_support.h"
+#endif
+
+namespace logger {
+
+#if defined(__ANDROID__)
+namespace {
+
+// __android_log_buf_write() is not exported in the NDK and is being used by
+// dynamic runtime linking. Its declaration is taken from Android's
+// system/core/include/log/log.h.
+using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char *tag,
+ const char *text);
+const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h.
+const char kAndroidLogTag[] = "google-breakpad";
+
+bool g_crash_log_initialized = false;
+AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr;
+
+} // namespace
+
+void initializeCrashLogWriter() {
+ if (g_crash_log_initialized)
+ return;
+ g_android_log_buf_write = reinterpret_cast<AndroidLogBufferWriteFunc>(
+ dlsym(RTLD_DEFAULT, "__android_log_buf_write"));
+ g_crash_log_initialized = true;
+}
+
+int writeToCrashLog(const char* buf) {
+ // Try writing to the crash log ring buffer. If not available, fall back to
+ // the standard log buffer.
+ if (g_android_log_buf_write) {
+ return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL,
+ kAndroidLogTag, buf);
+ }
+ return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf);
+}
+#endif
+
+int write(const char* buf, size_t nbytes) {
+#if defined(__ANDROID__)
+ return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf);
+#else
+ return sys_write(2, buf, nbytes);
+#endif
+}
+
+} // namespace logger
diff --git a/toolkit/crashreporter/breakpad-client/linux/log/log.h b/toolkit/crashreporter/breakpad-client/linux/log/log.h
new file mode 100644
index 0000000000..f94bbd5fb7
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/log/log.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef CLIENT_LINUX_LOG_LOG_H_
+#define CLIENT_LINUX_LOG_LOG_H_
+
+#include <stddef.h>
+
+namespace logger {
+
+int write(const char* buf, size_t nbytes);
+
+// In the case of Android the log can be written to the default system log
+// (default behavior of write() above, or to the crash log (see
+// writeToCrashLog() below).
+#if defined(__ANDROID__)
+
+// The logger must be initialized in a non-compromised context.
+void initializeCrashLogWriter();
+
+// Once initialized, writeToCrashLog is safe to use in a compromised context,
+// even if the initialization failed, in which case this will silently fall
+// back on write().
+int writeToCrashLog(const char* buf);
+#endif
+
+} // namespace logger
+
+#endif // CLIENT_LINUX_LOG_LOG_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc
new file mode 100644
index 0000000000..8f25b7be02
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc
@@ -0,0 +1,664 @@
+// 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.
+
+// This translation unit generates microdumps into the console (logcat on
+// Android). See crbug.com/410294 for more info and design docs.
+
+#include "linux/microdump_writer/microdump_writer.h"
+
+#include <limits>
+
+#include <sys/utsname.h>
+
+#include "linux/dump_writer_common/thread_info.h"
+#include "linux/dump_writer_common/ucontext_reader.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/handler/microdump_extra_info.h"
+#include "linux/log/log.h"
+#include "linux/minidump_writer/linux_ptrace_dumper.h"
+#include "common/linux/file_id.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/memory_allocator.h"
+
+namespace {
+
+using google_breakpad::auto_wasteful_vector;
+using google_breakpad::ExceptionHandler;
+using google_breakpad::kDefaultBuildIdSize;
+using google_breakpad::LinuxDumper;
+using google_breakpad::LinuxPtraceDumper;
+using google_breakpad::MappingInfo;
+using google_breakpad::MappingList;
+using google_breakpad::MicrodumpExtraInfo;
+using google_breakpad::RawContextCPU;
+using google_breakpad::ThreadInfo;
+using google_breakpad::UContextReader;
+
+const size_t kLineBufferSize = 2048;
+
+#if !defined(__LP64__)
+// The following are only used by DumpFreeSpace, so need to be compiled
+// in conditionally in the same way.
+
+template <typename Dst, typename Src>
+Dst saturated_cast(Src src) {
+ if (src >= std::numeric_limits<Dst>::max())
+ return std::numeric_limits<Dst>::max();
+ if (src <= std::numeric_limits<Dst>::min())
+ return std::numeric_limits<Dst>::min();
+ return static_cast<Dst>(src);
+}
+
+int Log2Floor(uint64_t n) {
+ // Copied from chromium src/base/bits.h
+ if (n == 0)
+ return -1;
+ int log = 0;
+ uint64_t value = n;
+ for (int i = 5; i >= 0; --i) {
+ int shift = (1 << i);
+ uint64_t x = value >> shift;
+ if (x != 0) {
+ value = x;
+ log += shift;
+ }
+ }
+ assert(value == 1u);
+ return log;
+}
+
+bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) {
+ // Because of load biasing, we can end up with a situation where two
+ // mappings actually overlap. So we will define adjacency to also include a
+ // b start address that lies within a's address range (including starting
+ // immediately after a).
+ // Because load biasing only ever moves the start address backwards, the end
+ // address should still increase.
+ return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr;
+}
+
+bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) {
+ // Return true if mapping a is before mapping b.
+ // For the same reason (load biasing) we compare end addresses, which - unlike
+ // start addresses - will not have been modified.
+ return a->start_addr + a->size < b->start_addr + b->size;
+}
+
+size_t NextOrderedMapping(
+ const google_breakpad::wasteful_vector<MappingInfo*>& mappings,
+ size_t curr) {
+ // Find the mapping that directly follows mappings[curr].
+ // If no such mapping exists, return |invalid| to indicate this.
+ const size_t invalid = std::numeric_limits<size_t>::max();
+ size_t best = invalid;
+ for (size_t next = 0; next < mappings.size(); ++next) {
+ if (MappingLessThan(mappings[curr], mappings[next]) &&
+ (best == invalid || MappingLessThan(mappings[next], mappings[best]))) {
+ best = next;
+ }
+ }
+ return best;
+}
+
+#endif // !__LP64__
+
+class MicrodumpWriter {
+ public:
+ MicrodumpWriter(const ExceptionHandler::CrashContext* context,
+ const MappingList& mappings,
+ bool skip_dump_if_principal_mapping_not_referenced,
+ uintptr_t address_within_principal_mapping,
+ bool sanitize_stack,
+ const MicrodumpExtraInfo& microdump_extra_info,
+ LinuxDumper* dumper)
+ : ucontext_(context ? &context->context : NULL),
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ float_state_(context ? &context->float_state : NULL),
+#endif
+ dumper_(dumper),
+ mapping_list_(mappings),
+ skip_dump_if_principal_mapping_not_referenced_(
+ skip_dump_if_principal_mapping_not_referenced),
+ address_within_principal_mapping_(address_within_principal_mapping),
+ sanitize_stack_(sanitize_stack),
+ microdump_extra_info_(microdump_extra_info),
+ log_line_(NULL),
+ stack_copy_(NULL),
+ stack_len_(0),
+ stack_lower_bound_(0),
+ stack_pointer_(0) {
+ log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
+ if (log_line_)
+ log_line_[0] = '\0'; // Clear out the log line buffer.
+ }
+
+ ~MicrodumpWriter() { dumper_->ThreadsResume(); }
+
+ bool Init() {
+ // In the exceptional case where the system was out of memory and there
+ // wasn't even room to allocate the line buffer, bail out. There is nothing
+ // useful we can possibly achieve without the ability to Log. At least let's
+ // try to not crash.
+ if (!dumper_->Init() || !log_line_)
+ return false;
+ return dumper_->ThreadsSuspend() && dumper_->LateInit();
+ }
+
+ void Dump() {
+ CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1);
+ if (stack_capture_result == CAPTURE_UNINTERESTING) {
+ LogLine("Microdump skipped (uninteresting)");
+ return;
+ }
+
+ LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
+ DumpProductInformation();
+ DumpOSInformation();
+ DumpProcessType();
+ DumpCrashReason();
+ DumpGPUInformation();
+#if !defined(__LP64__)
+ DumpFreeSpace();
+#endif
+ if (stack_capture_result == CAPTURE_OK)
+ DumpThreadStack();
+ DumpCPUState();
+ DumpMappings();
+ LogLine("-----END BREAKPAD MICRODUMP-----");
+ }
+
+ private:
+ enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING };
+
+ // Writes one line to the system log.
+ void LogLine(const char* msg) {
+#if defined(__ANDROID__)
+ logger::writeToCrashLog(msg);
+#else
+ logger::write(msg, my_strlen(msg));
+ logger::write("\n", 1);
+#endif
+ }
+
+ // Stages the given string in the current line buffer.
+ void LogAppend(const char* str) {
+ my_strlcat(log_line_, str, kLineBufferSize);
+ }
+
+ // As above (required to take precedence over template specialization below).
+ void LogAppend(char* str) {
+ LogAppend(const_cast<const char*>(str));
+ }
+
+ // Stages the hex repr. of the given int type in the current line buffer.
+ template<typename T>
+ void LogAppend(T value) {
+ // Make enough room to hex encode the largest int type + NUL.
+ static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'};
+ char hexstr[sizeof(T) * 2 + 1];
+ for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
+ hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
+ hexstr[sizeof(T) * 2] = '\0';
+ LogAppend(hexstr);
+ }
+
+ // Stages the buffer content hex-encoded in the current line buffer.
+ void LogAppend(const void* buf, size_t length) {
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
+ for (size_t i = 0; i < length; ++i, ++ptr)
+ LogAppend(*ptr);
+ }
+
+ // Writes out the current line buffer on the system log.
+ void LogCommitLine() {
+ LogLine(log_line_);
+ log_line_[0] = 0;
+ }
+
+ CaptureResult CaptureCrashingThreadStack(int max_stack_len) {
+ stack_pointer_ = UContextReader::GetStackPointer(ucontext_);
+
+ if (!dumper_->GetStackInfo(reinterpret_cast<const void**>(&stack_lower_bound_),
+ &stack_len_, stack_pointer_)) {
+ return CAPTURE_FAILED;
+ }
+
+ if (max_stack_len >= 0 &&
+ stack_len_ > static_cast<size_t>(max_stack_len)) {
+ stack_len_ = max_stack_len;
+ }
+
+ stack_copy_ = reinterpret_cast<uint8_t*>(Alloc(stack_len_));
+ dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(),
+ reinterpret_cast<const void*>(stack_lower_bound_),
+ stack_len_);
+
+ if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK;
+
+ const MappingInfo* principal_mapping =
+ dumper_->FindMappingNoBias(address_within_principal_mapping_);
+ if (!principal_mapping) return CAPTURE_UNINTERESTING;
+
+ uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr;
+ uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr;
+ uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
+ if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK;
+
+ if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_,
+ stack_pointer_ - stack_lower_bound_,
+ *principal_mapping)) {
+ return CAPTURE_OK;
+ }
+ return CAPTURE_UNINTERESTING;
+ }
+
+ void DumpProductInformation() {
+ LogAppend("V ");
+ if (microdump_extra_info_.product_info) {
+ LogAppend(microdump_extra_info_.product_info);
+ } else {
+ LogAppend("UNKNOWN:0.0.0.0");
+ }
+ LogCommitLine();
+ }
+
+ void DumpProcessType() {
+ LogAppend("P ");
+ if (microdump_extra_info_.process_type) {
+ LogAppend(microdump_extra_info_.process_type);
+ } else {
+ LogAppend("UNKNOWN");
+ }
+ LogCommitLine();
+ }
+
+ void DumpCrashReason() {
+ LogAppend("R ");
+ LogAppend(dumper_->crash_signal());
+ LogAppend(" ");
+ LogAppend(dumper_->GetCrashSignalString());
+ LogAppend(" ");
+ LogAppend(dumper_->crash_address());
+ LogCommitLine();
+ }
+
+ void DumpOSInformation() {
+ const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
+
+#if defined(__ANDROID__)
+ const char kOSId[] = "A";
+#else
+ const char kOSId[] = "L";
+#endif
+
+// Dump the runtime architecture. On multiarch devices it might not match the
+// hw architecture (the one returned by uname()), for instance in the case of
+// a 32-bit app running on a aarch64 device.
+#if defined(__aarch64__)
+ const char kArch[] = "arm64";
+#elif defined(__ARMEL__)
+ const char kArch[] = "arm";
+#elif defined(__x86_64__)
+ const char kArch[] = "x86_64";
+#elif defined(__i386__)
+ const char kArch[] = "x86";
+#elif defined(__mips__)
+# if _MIPS_SIM == _ABIO32
+ const char kArch[] = "mips";
+# elif _MIPS_SIM == _ABI64
+ const char kArch[] = "mips64";
+# else
+# error "This mips ABI is currently not supported (n32)"
+#endif
+#else
+#error "This code has not been ported to your platform yet"
+#endif
+
+ LogAppend("O ");
+ LogAppend(kOSId);
+ LogAppend(" ");
+ LogAppend(kArch);
+ LogAppend(" ");
+ LogAppend(n_cpus);
+ LogAppend(" ");
+
+ // Dump the HW architecture (e.g., armv7l, aarch64).
+ struct utsname uts;
+ const bool has_uts_info = (uname(&uts) == 0);
+ const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
+ LogAppend(hwArch);
+ LogAppend(" ");
+
+ // If the client has attached a build fingerprint to the MinidumpDescriptor
+ // use that one. Otherwise try to get some basic info from uname().
+ if (microdump_extra_info_.build_fingerprint) {
+ LogAppend(microdump_extra_info_.build_fingerprint);
+ } else if (has_uts_info) {
+ LogAppend(uts.release);
+ LogAppend(" ");
+ LogAppend(uts.version);
+ } else {
+ LogAppend("no build fingerprint available");
+ }
+ LogCommitLine();
+ }
+
+ void DumpGPUInformation() {
+ LogAppend("G ");
+ if (microdump_extra_info_.gpu_fingerprint) {
+ LogAppend(microdump_extra_info_.gpu_fingerprint);
+ } else {
+ LogAppend("UNKNOWN");
+ }
+ LogCommitLine();
+ }
+
+ void DumpThreadStack() {
+ if (sanitize_stack_) {
+ dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_,
+ stack_pointer_ - stack_lower_bound_);
+ }
+
+ LogAppend("S 0 ");
+ LogAppend(stack_pointer_);
+ LogAppend(" ");
+ LogAppend(stack_lower_bound_);
+ LogAppend(" ");
+ LogAppend(stack_len_);
+ LogCommitLine();
+
+ const size_t STACK_DUMP_CHUNK_SIZE = 384;
+ for (size_t stack_off = 0; stack_off < stack_len_;
+ stack_off += STACK_DUMP_CHUNK_SIZE) {
+ LogAppend("S ");
+ LogAppend(stack_lower_bound_ + stack_off);
+ LogAppend(" ");
+ LogAppend(stack_copy_ + stack_off,
+ std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off));
+ LogCommitLine();
+ }
+ }
+
+ void DumpCPUState() {
+ RawContextCPU cpu;
+ my_memset(&cpu, 0, sizeof(RawContextCPU));
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
+#else
+ UContextReader::FillCPUContext(&cpu, ucontext_);
+#endif
+ LogAppend("C ");
+ LogAppend(&cpu, sizeof(cpu));
+ LogCommitLine();
+ }
+
+ // If there is caller-provided information about this mapping
+ // in the mapping_list_ list, return true. Otherwise, return false.
+ bool HaveMappingInfo(const MappingInfo& mapping) {
+ for (MappingList::const_iterator iter = mapping_list_.begin();
+ iter != mapping_list_.end();
+ ++iter) {
+ // Ignore any mappings that are wholly contained within
+ // mappings in the mapping_info_ list.
+ if (mapping.start_addr >= iter->first.start_addr &&
+ (mapping.start_addr + mapping.size) <=
+ (iter->first.start_addr + iter->first.size)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Dump information about the provided |mapping|. If |identifier| is non-NULL,
+ // use it instead of calculating a file ID from the mapping.
+ void DumpModule(const MappingInfo& mapping,
+ bool member,
+ unsigned int mapping_id,
+ const std::vector<uint8_t>* identifier) {
+
+ auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
+ dumper_->allocator());
+
+ if (identifier) {
+ // GUID was provided by caller.
+ identifier_bytes.insert(identifier_bytes.end(),
+ identifier->begin(),
+ identifier->end());
+ } else {
+ dumper_->ElfFileIdentifierForMapping(
+ mapping,
+ member,
+ mapping_id,
+ identifier_bytes);
+ }
+
+ // Copy as many bytes of |identifier| as will fit into a MDGUID
+ MDGUID module_identifier = {0};
+ memcpy(&module_identifier, &identifier_bytes[0],
+ std::min(sizeof(MDGUID), identifier_bytes.size()));
+
+ char file_name[NAME_MAX];
+ char file_path[NAME_MAX];
+ dumper_->GetMappingEffectiveNameAndPath(
+ mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
+
+ LogAppend("M ");
+ LogAppend(static_cast<uintptr_t>(mapping.start_addr));
+ LogAppend(" ");
+ LogAppend(mapping.offset);
+ LogAppend(" ");
+ LogAppend(mapping.size);
+ LogAppend(" ");
+ LogAppend(module_identifier.data1);
+ LogAppend(module_identifier.data2);
+ LogAppend(module_identifier.data3);
+ LogAppend(module_identifier.data4[0]);
+ LogAppend(module_identifier.data4[1]);
+ LogAppend(module_identifier.data4[2]);
+ LogAppend(module_identifier.data4[3]);
+ LogAppend(module_identifier.data4[4]);
+ LogAppend(module_identifier.data4[5]);
+ LogAppend(module_identifier.data4[6]);
+ LogAppend(module_identifier.data4[7]);
+ LogAppend("0 "); // Age is always 0 on Linux.
+ LogAppend(file_name);
+ LogCommitLine();
+ }
+
+#if !defined(__LP64__)
+ void DumpFreeSpace() {
+ const MappingInfo* stack_mapping = nullptr;
+ ThreadInfo info;
+ if (dumper_->GetThreadInfoByIndex(dumper_->GetMainThreadIndex(), &info)) {
+ stack_mapping = dumper_->FindMappingNoBias(info.stack_pointer);
+ }
+
+ const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
+ dumper_->mappings();
+ if (mappings.size() == 0) return;
+
+ // This is complicated by the fact that mappings is not in order. It should
+ // be mostly in order, however the mapping that contains the entry point for
+ // the process is always at the front of the vector.
+
+ static const int HBITS = sizeof(size_t) * 8;
+ size_t hole_histogram[HBITS];
+ my_memset(hole_histogram, 0, sizeof(hole_histogram));
+
+ // Find the lowest address mapping.
+ size_t curr = 0;
+ for (size_t i = 1; i < mappings.size(); ++i) {
+ if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
+ }
+
+ uintptr_t lo_addr = mappings[curr]->start_addr;
+
+ size_t hole_cnt = 0;
+ size_t hole_max = 0;
+ size_t hole_sum = 0;
+
+ while (true) {
+ // Skip to the end of an adjacent run of mappings. This is an optimization
+ // for the fact that mappings is mostly sorted.
+ while (curr != mappings.size() - 1 &&
+ MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
+ ++curr;
+ }
+
+ if (mappings[curr] == stack_mapping) {
+ // Because we can't determine the top of userspace mappable
+ // memory we treat the start of the process stack as the top
+ // of the allocatable address space. Once we reach
+ // |stack_mapping| we are done scanning for free space regions.
+ break;
+ }
+
+ size_t next = NextOrderedMapping(mappings, curr);
+ if (next == std::numeric_limits<size_t>::max())
+ break;
+
+ uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
+ uintptr_t hole_hi = mappings[next]->start_addr;
+
+ if (hole_hi > hole_lo) {
+ size_t hole_sz = hole_hi - hole_lo;
+ hole_sum += hole_sz;
+ hole_max = std::max(hole_sz, hole_max);
+ ++hole_cnt;
+ ++hole_histogram[Log2Floor(hole_sz)];
+ }
+ curr = next;
+ }
+
+ uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
+
+ LogAppend("H ");
+ LogAppend(lo_addr);
+ LogAppend(" ");
+ LogAppend(hi_addr);
+ LogAppend(" ");
+ LogAppend(saturated_cast<uint16_t>(hole_cnt));
+ LogAppend(" ");
+ LogAppend(hole_max);
+ LogAppend(" ");
+ LogAppend(hole_sum);
+ for (unsigned int i = 0; i < HBITS; ++i) {
+ if (!hole_histogram[i]) continue;
+ LogAppend(" ");
+ LogAppend(saturated_cast<uint8_t>(i));
+ LogAppend(":");
+ LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
+ }
+ LogCommitLine();
+ }
+#endif
+
+ // Write information about the mappings in effect.
+ void DumpMappings() {
+ // First write all the mappings from the dumper
+ for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
+ const MappingInfo& mapping = *dumper_->mappings()[i];
+ if (mapping.name[0] == 0 || // only want modules with filenames.
+ !mapping.exec || // only want executable mappings.
+ mapping.size < 4096 || // too small to get a signature for.
+ HaveMappingInfo(mapping)) {
+ continue;
+ }
+
+ DumpModule(mapping, true, i, NULL);
+ }
+ // Next write all the mappings provided by the caller
+ for (MappingList::const_iterator iter = mapping_list_.begin();
+ iter != mapping_list_.end();
+ ++iter) {
+ DumpModule(iter->first, false, 0, &iter->second);
+ }
+ }
+
+ void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
+
+ const ucontext_t* const ucontext_;
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ const google_breakpad::fpstate_t* const float_state_;
+#endif
+ LinuxDumper* dumper_;
+ const MappingList& mapping_list_;
+ bool skip_dump_if_principal_mapping_not_referenced_;
+ uintptr_t address_within_principal_mapping_;
+ bool sanitize_stack_;
+ const MicrodumpExtraInfo microdump_extra_info_;
+ char* log_line_;
+
+ // The local copy of crashed process stack memory, beginning at
+ // |stack_lower_bound_|.
+ uint8_t* stack_copy_;
+
+ // The length of crashed process stack copy.
+ size_t stack_len_;
+
+ // The address of the page containing the stack pointer in the
+ // crashed process. |stack_lower_bound_| <= |stack_pointer_|
+ uintptr_t stack_lower_bound_;
+
+ // The stack pointer of the crashed thread.
+ uintptr_t stack_pointer_;
+};
+} // namespace
+
+namespace google_breakpad {
+
+bool WriteMicrodump(pid_t crashing_process,
+ const void* blob,
+ size_t blob_size,
+ const MappingList& mappings,
+ bool skip_dump_if_principal_mapping_not_referenced,
+ uintptr_t address_within_principal_mapping,
+ bool sanitize_stack,
+ const MicrodumpExtraInfo& microdump_extra_info) {
+ LinuxPtraceDumper dumper(crashing_process);
+ const ExceptionHandler::CrashContext* context = NULL;
+ if (blob) {
+ if (blob_size != sizeof(ExceptionHandler::CrashContext))
+ return false;
+ context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
+ dumper.SetCrashInfoFromSigInfo(context->siginfo);
+ dumper.set_crash_thread(context->tid);
+ }
+ MicrodumpWriter writer(context, mappings,
+ skip_dump_if_principal_mapping_not_referenced,
+ address_within_principal_mapping, sanitize_stack,
+ microdump_extra_info, &dumper);
+ if (!writer.Init())
+ return false;
+ writer.Dump();
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.h b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.h
new file mode 100644
index 0000000000..63b5463247
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "linux/dump_writer_common/mapping_info.h"
+
+namespace google_breakpad {
+
+struct MicrodumpExtraInfo;
+
+// Writes a microdump (a reduced dump containing only the state of the crashing
+// thread) on the console (logcat on Android). These functions do not malloc nor
+// use libc functions which may. Thus, it can be used in contexts where the
+// state of the heap may be corrupt.
+// Args:
+// crashing_process: the pid of the crashing process. This must be trusted.
+// blob: a blob of data from the crashing process. See exception_handler.h
+// blob_size: the length of |blob| in bytes.
+// mappings: a list of additional mappings provided by the application.
+// build_fingerprint: a (optional) C string which determines the OS
+// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys).
+// product_info: a (optional) C string which determines the product name and
+// version (e.g., WebView:42.0.2311.136).
+//
+// Returns true iff successful.
+bool WriteMicrodump(pid_t crashing_process,
+ const void* blob,
+ size_t blob_size,
+ const MappingList& mappings,
+ bool skip_dump_if_main_module_not_referenced,
+ uintptr_t address_within_main_module,
+ bool sanitize_stack,
+ const MicrodumpExtraInfo& microdump_extra_info);
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer_unittest.cc
new file mode 100644
index 0000000000..6b7fb5f6ac
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer_unittest.cc
@@ -0,0 +1,421 @@
+// 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.
+
+#include <ctype.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ucontext.h>
+
+#include <sstream>
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/handler/microdump_extra_info.h"
+#include "linux/microdump_writer/microdump_writer.h"
+#include "common/linux/breakpad_getcontext.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/ignore_ret.h"
+#include "common/scoped_ptr.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/using_std_string.h"
+
+using namespace google_breakpad;
+
+extern "C" {
+extern char __executable_start;
+extern char __etext;
+}
+
+namespace {
+
+typedef testing::Test MicrodumpWriterTest;
+
+MicrodumpExtraInfo MakeMicrodumpExtraInfo(
+ const char* build_fingerprint,
+ const char* product_info,
+ const char* gpu_fingerprint) {
+ MicrodumpExtraInfo info;
+ info.build_fingerprint = build_fingerprint;
+ info.product_info = product_info;
+ info.gpu_fingerprint = gpu_fingerprint;
+ info.process_type = "Browser";
+ return info;
+}
+
+bool ContainsMicrodump(const std::string& buf) {
+ return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") &&
+ std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----");
+}
+
+const char kIdentifiableString[] = "_IDENTIFIABLE_";
+const uintptr_t kCrashAddress = 0xdeaddeadu;
+
+void CrashAndGetMicrodump(const MappingList& mappings,
+ const MicrodumpExtraInfo& microdump_extra_info,
+ std::string* microdump,
+ bool skip_dump_if_principal_mapping_not_referenced = false,
+ uintptr_t address_within_principal_mapping = 0,
+ bool sanitize_stack = false) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ AutoTempDir temp_dir;
+ string stderr_file = temp_dir.path() + "/stderr.log";
+ int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+ ASSERT_NE(-1, err_fd);
+
+ char identifiable_string[sizeof(kIdentifiableString)];
+
+ // This string should not appear in the resulting microdump if it
+ // has been sanitized.
+ strcpy(identifiable_string, kIdentifiableString);
+ // Force the strcpy to not be optimized away.
+ IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ // Pretend the current context is the child context (which is
+ // approximately right) so that we have a valid stack pointer, and
+ // can fetch child stack data via ptrace.
+ getcontext(&context.context);
+ // Set a non-zero tid to avoid tripping asserts.
+ context.tid = child;
+ context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
+ context.siginfo.si_addr = reinterpret_cast<void*>(kCrashAddress);
+
+ // Redirect temporarily stderr to the stderr.log file.
+ int save_err = dup(STDERR_FILENO);
+ ASSERT_NE(-1, save_err);
+ ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
+
+ ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
+ skip_dump_if_principal_mapping_not_referenced,
+ address_within_principal_mapping, sanitize_stack,
+ microdump_extra_info));
+
+ // Revert stderr back to the console.
+ dup2(save_err, STDERR_FILENO);
+ close(save_err);
+
+ // Read back the stderr file and check for the microdump marker.
+ fsync(err_fd);
+ lseek(err_fd, 0, SEEK_SET);
+
+ microdump->clear();
+ char buf[1024];
+
+ while (true) {
+ int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024));
+ if (bytes_read <= 0) break;
+ microdump->append(buf, buf + bytes_read);
+ }
+ close(err_fd);
+ close(fds[1]);
+}
+
+void ExtractMicrodumpStackContents(const string& microdump_content,
+ string* result) {
+ std::istringstream iss(microdump_content);
+ result->clear();
+ for (string line; std::getline(iss, line);) {
+ if (line.find("S ") == 0) {
+ std::istringstream stack_data(line);
+ std::string key;
+ std::string addr;
+ std::string data;
+ stack_data >> key >> addr >> data;
+ EXPECT_TRUE((data.size() & 1u) == 0u);
+ result->reserve(result->size() + data.size() / 2);
+ for (size_t i = 0; i < data.size(); i += 2) {
+ std::string byte = data.substr(i, 2);
+ result->push_back(static_cast<char>(strtoul(byte.c_str(), NULL, 16)));
+ }
+ }
+ }
+}
+
+void CheckMicrodumpContents(const string& microdump_content,
+ const MicrodumpExtraInfo& expected_info) {
+ std::istringstream iss(microdump_content);
+ bool did_find_os_info = false;
+ bool did_find_product_info = false;
+ bool did_find_process_type = false;
+ bool did_find_crash_reason = false;
+ bool did_find_gpu_info = false;
+ for (string line; std::getline(iss, line);) {
+ if (line.find("O ") == 0) {
+ std::istringstream os_info_tokens(line);
+ string token;
+ os_info_tokens.ignore(2); // Ignore the "O " preamble.
+ // Check the OS descriptor char (L=Linux, A=Android).
+ os_info_tokens >> token;
+ ASSERT_TRUE(token == "L" || token == "A");
+
+ os_info_tokens >> token; // HW architecture.
+ os_info_tokens >> token; // Number of cpus.
+ for (size_t i = 0; i < token.size(); ++i)
+ ASSERT_TRUE(isxdigit(token[i]));
+ os_info_tokens >> token; // SW architecture.
+
+ // Check that the build fingerprint is in the right place.
+ os_info_tokens >> token;
+ ASSERT_FALSE(os_info_tokens.fail());
+ if (expected_info.build_fingerprint)
+ ASSERT_EQ(expected_info.build_fingerprint, token);
+ did_find_os_info = true;
+ } else if (line.find("P ") == 0) {
+ if (expected_info.process_type)
+ ASSERT_EQ(string("P ") + expected_info.process_type, line);
+ did_find_process_type = true;
+ } else if (line.find("R ") == 0) {
+ std::istringstream crash_reason_tokens(line);
+ string token;
+ unsigned crash_reason;
+ string crash_reason_str;
+ uintptr_t crash_address;
+ crash_reason_tokens.ignore(2); // Ignore the "R " preamble.
+ crash_reason_tokens >> std::hex >> crash_reason >> crash_reason_str >>
+ crash_address;
+ ASSERT_FALSE(crash_reason_tokens.fail());
+ ASSERT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, crash_reason);
+ ASSERT_EQ("DUMP_REQUESTED", crash_reason_str);
+ ASSERT_EQ(kCrashAddress, crash_address);
+ did_find_crash_reason = true;
+ } else if (line.find("V ") == 0) {
+ if (expected_info.product_info)
+ ASSERT_EQ(string("V ") + expected_info.product_info, line);
+ did_find_product_info = true;
+ } else if (line.find("G ") == 0) {
+ if (expected_info.gpu_fingerprint)
+ ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line);
+ did_find_gpu_info = true;
+ }
+ }
+ ASSERT_TRUE(did_find_os_info);
+ ASSERT_TRUE(did_find_product_info);
+ ASSERT_TRUE(did_find_process_type);
+ ASSERT_TRUE(did_find_crash_reason);
+ ASSERT_TRUE(did_find_gpu_info);
+}
+
+bool MicrodumpStackContains(const string& microdump_content,
+ const string& expected_content) {
+ string result;
+ ExtractMicrodumpStackContents(microdump_content, &result);
+ return result.find(kIdentifiableString) != string::npos;
+}
+
+void CheckMicrodumpContents(const string& microdump_content,
+ const string& expected_fingerprint,
+ const string& expected_product_info,
+ const string& expected_gpu_fingerprint) {
+ CheckMicrodumpContents(
+ microdump_content,
+ MakeMicrodumpExtraInfo(expected_fingerprint.c_str(),
+ expected_product_info.c_str(),
+ expected_gpu_fingerprint.c_str()));
+}
+
+TEST(MicrodumpWriterTest, BasicWithMappings) {
+ // Push some extra mapping to check the MappingList logic.
+ const uint32_t memory_size = sysconf(_SC_PAGESIZE);
+ const char* kMemoryName = "libfoo.so";
+ const uint8_t kModuleGUID[sizeof(MDGUID)] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+ };
+
+ MappingInfo info;
+ info.start_addr = memory_size;
+ info.size = memory_size;
+ info.offset = 42;
+ strcpy(info.name, kMemoryName);
+
+ MappingList mappings;
+ MappingEntry mapping;
+ mapping.first = info;
+ mapping.second.assign(std::begin(kModuleGUID), std::end(kModuleGUID));
+ mappings.push_back(mapping);
+
+ std::string buf;
+ CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+
+#ifdef __LP64__
+ ASSERT_NE(std::string::npos,
+ buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
+ "33221100554477668899AABBCCDDEEFF0 libfoo.so"));
+#else
+ ASSERT_NE(std::string::npos,
+ buf.find("M 00001000 0000002A 00001000 "
+ "33221100554477668899AABBCCDDEEFF0 libfoo.so"));
+#endif
+
+ // In absence of a product info in the minidump, the writer should just write
+ // an unknown marker.
+ ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0"));
+}
+
+// Ensure that no output occurs if the interest region is set, but
+// doesn't overlap anything on the stack.
+TEST(MicrodumpWriterTest, NoOutputIfUninteresting) {
+ const char kProductInfo[] = "MockProduct:42.0.2311.99";
+ const char kBuildFingerprint[] =
+ "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
+ const char kGPUFingerprint[] =
+ "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
+ const MicrodumpExtraInfo kMicrodumpExtraInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
+
+ std::string buf;
+ MappingList no_mappings;
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0);
+ ASSERT_FALSE(ContainsMicrodump(buf));
+}
+
+// Ensure that stack content does not contain an identifiable string if the
+// stack is sanitized.
+TEST(MicrodumpWriterTest, StringRemovedBySanitization) {
+ const char kProductInfo[] = "MockProduct:42.0.2311.99";
+ const char kBuildFingerprint[] =
+ "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
+ const char kGPUFingerprint[] =
+ "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
+
+ const MicrodumpExtraInfo kMicrodumpExtraInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
+
+ std::string buf;
+ MappingList no_mappings;
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+ ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString));
+}
+
+// Ensure that stack content does contain an identifiable string if the
+// stack is not sanitized.
+TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) {
+ const char kProductInfo[] = "MockProduct:42.0.2311.99";
+ const char kBuildFingerprint[] =
+ "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
+ const char kGPUFingerprint[] =
+ "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
+
+ const MicrodumpExtraInfo kMicrodumpExtraInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
+
+ std::string buf;
+ MappingList no_mappings;
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+ ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString));
+}
+
+// Ensure that output occurs if the interest region is set, and
+// does overlap something on the stack.
+TEST(MicrodumpWriterTest, OutputIfInteresting) {
+ const char kProductInfo[] = "MockProduct:42.0.2311.99";
+ const char kBuildFingerprint[] =
+ "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
+ const char kGPUFingerprint[] =
+ "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
+
+ const MicrodumpExtraInfo kMicrodumpExtraInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
+
+ std::string buf;
+ MappingList no_mappings;
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true,
+ reinterpret_cast<uintptr_t>(CrashAndGetMicrodump));
+ ASSERT_TRUE(ContainsMicrodump(buf));
+}
+
+// Ensure that the product info and build fingerprint metadata show up in the
+// final microdump if present.
+TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
+ const char kProductInfo[] = "MockProduct:42.0.2311.99";
+ const char kBuildFingerprint[] =
+ "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
+ const char kGPUFingerprint[] =
+ "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
+ const MicrodumpExtraInfo kMicrodumpExtraInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
+ std::string buf;
+ MappingList no_mappings;
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+ CheckMicrodumpContents(buf, kMicrodumpExtraInfo);
+}
+
+TEST(MicrodumpWriterTest, NoProductInfo) {
+ const char kBuildFingerprint[] = "foobar";
+ const char kGPUFingerprint[] = "bazqux";
+ std::string buf;
+ MappingList no_mappings;
+
+ const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+ CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0",
+ kGPUFingerprint);
+}
+
+TEST(MicrodumpWriterTest, NoGPUInfo) {
+ const char kProductInfo[] = "bazqux";
+ const char kBuildFingerprint[] = "foobar";
+ std::string buf;
+ MappingList no_mappings;
+
+ const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
+ MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
+
+ CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
+ ASSERT_TRUE(ContainsMicrodump(buf));
+ CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN");
+}
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set.h
new file mode 100644
index 0000000000..1cca9aa5a0
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set.h
@@ -0,0 +1,144 @@
+// 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// Helper class used to model a set of CPUs, as read from sysfs
+// files like /sys/devices/system/cpu/present
+// See See http://www.kernel.org/doc/Documentation/cputopology.txt
+class CpuSet {
+public:
+ // The maximum number of supported CPUs.
+ static const size_t kMaxCpus = 1024;
+
+ CpuSet() {
+ my_memset(mask_, 0, sizeof(mask_));
+ }
+
+ // Parse a sysfs file to extract the corresponding CPU set.
+ bool ParseSysFile(int fd) {
+ char buffer[512];
+ int ret = sys_read(fd, buffer, sizeof(buffer)-1);
+ if (ret < 0)
+ return false;
+
+ buffer[ret] = '\0';
+
+ // Expected format: comma-separated list of items, where each
+ // item can be a decimal integer, or two decimal integers separated
+ // by a dash.
+ // E.g.:
+ // 0
+ // 0,1,2,3
+ // 0-3
+ // 1,10-23
+ const char* p = buffer;
+ const char* p_end = p + ret;
+ while (p < p_end) {
+ // Skip leading space, if any
+ while (p < p_end && my_isspace(*p))
+ p++;
+
+ // Find start and size of current item.
+ const char* item = p;
+ size_t item_len = static_cast<size_t>(p_end - p);
+ const char* item_next =
+ static_cast<const char*>(my_memchr(p, ',', item_len));
+ if (item_next != NULL) {
+ p = item_next + 1;
+ item_len = static_cast<size_t>(item_next - item);
+ } else {
+ p = p_end;
+ item_next = p_end;
+ }
+
+ // Ignore trailing spaces.
+ while (item_next > item && my_isspace(item_next[-1]))
+ item_next--;
+
+ // skip empty items.
+ if (item_next == item)
+ continue;
+
+ // read first decimal value.
+ uintptr_t start = 0;
+ const char* next = my_read_decimal_ptr(&start, item);
+ uintptr_t end = start;
+ if (*next == '-')
+ my_read_decimal_ptr(&end, next+1);
+
+ while (start <= end)
+ SetBit(start++);
+ }
+ return true;
+ }
+
+ // Intersect this CPU set with another one.
+ void IntersectWith(const CpuSet& other) {
+ for (size_t nn = 0; nn < kMaskWordCount; ++nn)
+ mask_[nn] &= other.mask_[nn];
+ }
+
+ // Return the number of CPUs in this set.
+ int GetCount() {
+ int result = 0;
+ for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
+ result += __builtin_popcount(mask_[nn]);
+ }
+ return result;
+ }
+
+private:
+ void SetBit(uintptr_t index) {
+ size_t nn = static_cast<size_t>(index);
+ if (nn < kMaxCpus)
+ mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
+ }
+
+ typedef uint32_t MaskWordType;
+ static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
+ static const size_t kMaskWordCount =
+ (kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
+
+ MaskWordType mask_[kMaskWordCount];
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set_unittest.cc
new file mode 100644
index 0000000000..75172e9938
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/cpu_set_unittest.cc
@@ -0,0 +1,164 @@
+// 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.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/minidump_writer/cpu_set.h"
+#include "common/linux/tests/auto_testfile.h"
+
+using namespace google_breakpad;
+
+namespace {
+
+typedef testing::Test CpuSetTest;
+
+// Helper class to write test text file to a temporary file and return
+// its file descriptor.
+class ScopedTestFile : public AutoTestFile {
+public:
+ explicit ScopedTestFile(const char* text)
+ : AutoTestFile("cpu_set", text) {
+ }
+};
+
+}
+
+TEST(CpuSetTest, EmptyCount) {
+ CpuSet set;
+ ASSERT_EQ(0, set.GetCount());
+}
+
+TEST(CpuSetTest, OneCpu) {
+ ScopedTestFile file("10");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(1, set.GetCount());
+}
+
+TEST(CpuSetTest, OneCpuTerminated) {
+ ScopedTestFile file("10\n");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(1, set.GetCount());
+}
+
+TEST(CpuSetTest, TwoCpusWithComma) {
+ ScopedTestFile file("1,10");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(2, set.GetCount());
+}
+
+TEST(CpuSetTest, TwoCpusWithRange) {
+ ScopedTestFile file("1-2");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(2, set.GetCount());
+}
+
+TEST(CpuSetTest, TenCpusWithRange) {
+ ScopedTestFile file("9-18");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(10, set.GetCount());
+}
+
+TEST(CpuSetTest, MultiItems) {
+ ScopedTestFile file("0, 2-4, 128");
+ ASSERT_TRUE(file.IsOk());
+
+ CpuSet set;
+ ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
+ ASSERT_EQ(5, set.GetCount());
+}
+
+TEST(CpuSetTest, IntersectWith) {
+ ScopedTestFile file1("9-19");
+ ASSERT_TRUE(file1.IsOk());
+ CpuSet set1;
+ ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
+ ASSERT_EQ(11, set1.GetCount());
+
+ ScopedTestFile file2("16-24");
+ ASSERT_TRUE(file2.IsOk());
+ CpuSet set2;
+ ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
+ ASSERT_EQ(9, set2.GetCount());
+
+ set1.IntersectWith(set2);
+ ASSERT_EQ(4, set1.GetCount());
+ ASSERT_EQ(9, set2.GetCount());
+}
+
+TEST(CpuSetTest, SelfIntersection) {
+ ScopedTestFile file1("9-19");
+ ASSERT_TRUE(file1.IsOk());
+ CpuSet set1;
+ ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
+ ASSERT_EQ(11, set1.GetCount());
+
+ set1.IntersectWith(set1);
+ ASSERT_EQ(11, set1.GetCount());
+}
+
+TEST(CpuSetTest, EmptyIntersection) {
+ ScopedTestFile file1("0-19");
+ ASSERT_TRUE(file1.IsOk());
+ CpuSet set1;
+ ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
+ ASSERT_EQ(20, set1.GetCount());
+
+ ScopedTestFile file2("20-39");
+ ASSERT_TRUE(file2.IsOk());
+ CpuSet set2;
+ ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
+ ASSERT_EQ(20, set2.GetCount());
+
+ set1.IntersectWith(set2);
+ ASSERT_EQ(0, set1.GetCount());
+
+ ASSERT_EQ(20, set2.GetCount());
+}
+
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader.h
new file mode 100644
index 0000000000..a4bde18031
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2009, 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for enumerating a directory without using diropen/readdir or other
+// functions which may allocate memory.
+class DirectoryReader {
+ public:
+ DirectoryReader(int fd)
+ : fd_(fd),
+ buf_used_(0) {
+ }
+
+ // Return the next entry from the directory
+ // name: (output) the NUL terminated entry name
+ //
+ // Returns true iff successful (false on EOF).
+ //
+ // After calling this, one must call |PopEntry| otherwise you'll get the same
+ // entry over and over.
+ bool GetNextEntry(const char** name) {
+ struct kernel_dirent* const dent =
+ reinterpret_cast<kernel_dirent*>(buf_);
+
+ if (buf_used_ == 0) {
+ // need to read more entries.
+ const int n = sys_getdents(fd_, dent, sizeof(buf_));
+ if (n < 0) {
+ return false;
+ } else if (n == 0) {
+ hit_eof_ = true;
+ } else {
+ buf_used_ += n;
+ }
+ }
+
+ if (buf_used_ == 0 && hit_eof_)
+ return false;
+
+ assert(buf_used_ > 0);
+
+ *name = dent->d_name;
+ return true;
+ }
+
+ void PopEntry() {
+ if (!buf_used_)
+ return;
+
+ const struct kernel_dirent* const dent =
+ reinterpret_cast<kernel_dirent*>(buf_);
+
+ buf_used_ -= dent->d_reclen;
+ my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
+ }
+
+ private:
+ const int fd_;
+ bool hit_eof_;
+ unsigned buf_used_;
+ uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader_unittest.cc
new file mode 100644
index 0000000000..b33507db95
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/directory_reader_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2009, 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 <set>
+#include <string>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "linux/minidump_writer/directory_reader.h"
+#include "common/using_std_string.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test DirectoryReaderTest;
+}
+
+TEST(DirectoryReaderTest, CompareResults) {
+ std::set<string> dent_set;
+
+ DIR *const dir = opendir("/proc/self");
+ ASSERT_TRUE(dir != NULL);
+
+ struct dirent* dent;
+ while ((dent = readdir(dir)))
+ dent_set.insert(dent->d_name);
+
+ closedir(dir);
+
+ const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
+ ASSERT_GE(fd, 0);
+
+ DirectoryReader dir_reader(fd);
+ unsigned seen = 0;
+
+ const char* name;
+ while (dir_reader.GetNextEntry(&name)) {
+ ASSERT_TRUE(dent_set.find(name) != dent_set.end());
+ seen++;
+ dir_reader.PopEntry();
+ }
+
+ ASSERT_TRUE(dent_set.find("status") != dent_set.end());
+ ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
+ ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
+
+ ASSERT_EQ(dent_set.size(), seen);
+ close(fd);
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader.h
new file mode 100644
index 0000000000..d8e2dbcc11
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2009, 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for reading a file, line by line, without using fopen/fgets or other
+// functions which may allocate memory.
+class LineReader {
+ public:
+ LineReader(int fd)
+ : fd_(fd),
+ hit_eof_(false),
+ buf_used_(0) {
+ }
+
+ // The maximum length of a line.
+ static const size_t kMaxLineLen = 1024;
+
+ // Return the next line from the file.
+ // line: (output) a pointer to the start of the line. The line is NUL
+ // terminated.
+ // len: (output) the length of the line (not inc the NUL byte)
+ //
+ // Returns true iff successful (false on EOF).
+ //
+ // One must call |PopLine| after this function, otherwise you'll continue to
+ // get the same line over and over.
+ bool GetNextLine(const char **line, unsigned *len) {
+ for (;;) {
+ if (buf_used_ == 0 && hit_eof_)
+ return false;
+
+ for (unsigned i = 0; i < buf_used_; ++i) {
+ if (buf_[i] == '\n' || buf_[i] == 0) {
+ buf_[i] = 0;
+ *len = i;
+ *line = buf_;
+ return true;
+ }
+ }
+
+ if (buf_used_ == sizeof(buf_)) {
+ // we scanned the whole buffer and didn't find an end-of-line marker.
+ // This line is too long to process.
+ return false;
+ }
+
+ // We didn't find any end-of-line terminators in the buffer. However, if
+ // this is the last line in the file it might not have one:
+ if (hit_eof_) {
+ assert(buf_used_);
+ // There's room for the NUL because of the buf_used_ == sizeof(buf_)
+ // check above.
+ buf_[buf_used_] = 0;
+ *len = buf_used_;
+ buf_used_ += 1; // since we appended the NUL.
+ *line = buf_;
+ return true;
+ }
+
+ // Otherwise, we should pull in more data from the file
+ const ssize_t n = sys_read(fd_, buf_ + buf_used_,
+ sizeof(buf_) - buf_used_);
+ if (n < 0) {
+ return false;
+ } else if (n == 0) {
+ hit_eof_ = true;
+ } else {
+ buf_used_ += n;
+ }
+
+ // At this point, we have either set the hit_eof_ flag, or we have more
+ // data to process...
+ }
+ }
+
+ void PopLine(unsigned len) {
+ // len doesn't include the NUL byte at the end.
+
+ assert(buf_used_ >= len + 1);
+ buf_used_ -= len + 1;
+ my_memmove(buf_, buf_ + len + 1, buf_used_);
+ }
+
+ private:
+ const int fd_;
+
+ bool hit_eof_;
+ unsigned buf_used_;
+ char buf_[kMaxLineLen];
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader_unittest.cc
new file mode 100644
index 0000000000..576b8b5cdb
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/line_reader_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2009, 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 <sys/types.h>
+
+#include "linux/minidump_writer/line_reader.h"
+#include "breakpad_googletest_includes.h"
+#include "common/linux/tests/auto_testfile.h"
+
+using namespace google_breakpad;
+
+namespace {
+
+typedef testing::Test LineReaderTest;
+
+class ScopedTestFile : public AutoTestFile {
+public:
+ explicit ScopedTestFile(const char* text)
+ : AutoTestFile("line_reader", text) {
+ }
+
+ ScopedTestFile(const char* text, size_t text_len)
+ : AutoTestFile("line_reader", text, text_len) {
+ }
+};
+
+}
+
+TEST(LineReaderTest, EmptyFile) {
+ ScopedTestFile file("");
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
+
+TEST(LineReaderTest, OneLineTerminated) {
+ ScopedTestFile file("a\n");
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned int len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned int)1, len);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
+
+TEST(LineReaderTest, OneLine) {
+ ScopedTestFile file("a");
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned)1, len);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
+
+TEST(LineReaderTest, TwoLinesTerminated) {
+ ScopedTestFile file("a\nb\n");
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned)1, len);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned)1, len);
+ ASSERT_EQ('b', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
+
+TEST(LineReaderTest, TwoLines) {
+ ScopedTestFile file("a\nb");
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned)1, len);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ((unsigned)1, len);
+ ASSERT_EQ('b', line[0]);
+ ASSERT_EQ('\0', line[1]);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
+
+TEST(LineReaderTest, MaxLength) {
+ char l[LineReader::kMaxLineLen-1];
+ memset(l, 'a', sizeof(l));
+ ScopedTestFile file(l, sizeof(l));
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(sizeof(l), len);
+ ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
+ ASSERT_EQ('\0', line[len]);
+}
+
+TEST(LineReaderTest, TooLong) {
+ // Note: this writes kMaxLineLen 'a' chars in the test file.
+ char l[LineReader::kMaxLineLen];
+ memset(l, 'a', sizeof(l));
+ ScopedTestFile file(l, sizeof(l));
+ ASSERT_TRUE(file.IsOk());
+ LineReader reader(file.GetFd());
+
+ const char *line;
+ unsigned len;
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.cc
new file mode 100644
index 0000000000..38feb0aaa7
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.cc
@@ -0,0 +1,308 @@
+// 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.
+
+// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
+// See linux_core_dumper.h for details.
+
+#include "linux/minidump_writer/linux_core_dumper.h"
+
+#include <asm/ptrace.h>
+#include <assert.h>
+#include <elf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/procfs.h>
+#if defined(__mips__) && defined(__ANDROID__)
+// To get register definitions.
+#include <asm/reg.h>
+#endif
+
+#include "common/linux/elf_gnu_compat.h"
+#include "common/linux/linux_libc_support.h"
+
+namespace google_breakpad {
+
+LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
+ const char* core_path,
+ const char* procfs_path,
+ const char* root_prefix)
+ : LinuxDumper(pid, root_prefix),
+ core_path_(core_path),
+ procfs_path_(procfs_path),
+ thread_infos_(&allocator_, 8) {
+ assert(core_path_);
+}
+
+bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
+ const char* node) const {
+ if (!path || !node)
+ return false;
+
+ size_t node_len = my_strlen(node);
+ if (node_len == 0)
+ return false;
+
+ size_t procfs_path_len = my_strlen(procfs_path_);
+ size_t total_length = procfs_path_len + 1 + node_len;
+ if (total_length >= NAME_MAX)
+ return false;
+
+ memcpy(path, procfs_path_, procfs_path_len);
+ path[procfs_path_len] = '/';
+ memcpy(path + procfs_path_len + 1, node, node_len);
+ path[total_length] = '\0';
+ return true;
+}
+
+bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
+ const void* src, size_t length) {
+ ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
+ // TODO(benchan): Investigate whether the data to be copied could span
+ // across multiple segments in the core dump file. ElfCoreDump::CopyData
+ // and this method do not handle that case yet.
+ if (!core_.CopyData(dest, virtual_address, length)) {
+ // If the data segment is not found in the core dump, fill the result
+ // with marker characters.
+ memset(dest, 0xab, length);
+ return false;
+ }
+ return true;
+}
+
+bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
+ if (index >= thread_infos_.size())
+ return false;
+
+ *info = thread_infos_[index];
+ const uint8_t* stack_pointer;
+#if defined(__i386)
+ memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
+#elif defined(__x86_64)
+ memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
+#elif defined(__ARM_EABI__)
+ memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
+#elif defined(__aarch64__)
+ memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
+#elif defined(__mips__)
+ stack_pointer =
+ reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
+#else
+#error "This code hasn't been ported to your platform yet."
+#endif
+ info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
+ return true;
+}
+
+bool LinuxCoreDumper::GetThreadNameByIndex(size_t index, char* name,
+ size_t size) {
+ // Not implemented
+ return false;
+}
+
+bool LinuxCoreDumper::IsPostMortem() const {
+ return true;
+}
+
+bool LinuxCoreDumper::ThreadsSuspend() {
+ return true;
+}
+
+bool LinuxCoreDumper::ThreadsResume() {
+ return true;
+}
+
+bool LinuxCoreDumper::EnumerateThreads() {
+ if (!mapped_core_file_.Map(core_path_, 0)) {
+ fprintf(stderr, "Could not map core dump file into memory\n");
+ return false;
+ }
+
+ core_.SetContent(mapped_core_file_.content());
+ if (!core_.IsValid()) {
+ fprintf(stderr, "Invalid core dump file\n");
+ return false;
+ }
+
+ ElfCoreDump::Note note = core_.GetFirstNote();
+ if (!note.IsValid()) {
+ fprintf(stderr, "PT_NOTE section not found\n");
+ return false;
+ }
+
+ bool first_thread = true;
+ do {
+ ElfCoreDump::Word type = note.GetType();
+ MemoryRange name = note.GetName();
+ MemoryRange description = note.GetDescription();
+
+ if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
+ fprintf(stderr, "Could not found a valid PT_NOTE.\n");
+ return false;
+ }
+
+ // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
+ // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
+ // Thread Name Type
+ // -------------------------------------------------------------------
+ // 1st thread CORE NT_PRSTATUS
+ // process-wide CORE NT_PRPSINFO
+ // process-wide CORE NT_SIGINFO
+ // process-wide CORE NT_AUXV
+ // 1st thread CORE NT_FPREGSET
+ // 1st thread LINUX NT_PRXFPREG
+ // 1st thread LINUX NT_386_TLS
+ //
+ // 2nd thread CORE NT_PRSTATUS
+ // 2nd thread CORE NT_FPREGSET
+ // 2nd thread LINUX NT_PRXFPREG
+ // 2nd thread LINUX NT_386_TLS
+ //
+ // 3rd thread CORE NT_PRSTATUS
+ // 3rd thread CORE NT_FPREGSET
+ // 3rd thread LINUX NT_PRXFPREG
+ // 3rd thread LINUX NT_386_TLS
+ //
+ // The following code only works if notes are ordered as expected.
+ switch (type) {
+ case NT_PRSTATUS: {
+ if (description.length() != sizeof(elf_prstatus)) {
+ fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
+ return false;
+ }
+
+ const elf_prstatus* status =
+ reinterpret_cast<const elf_prstatus*>(description.data());
+ pid_t pid = status->pr_pid;
+ ThreadInfo info;
+ memset(&info, 0, sizeof(ThreadInfo));
+ info.tgid = status->pr_pgrp;
+ info.ppid = status->pr_ppid;
+#if defined(__mips__)
+#if defined(__ANDROID__)
+ for (int i = EF_R0; i <= EF_R31; i++)
+ info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
+#else // __ANDROID__
+ for (int i = EF_REG0; i <= EF_REG31; i++)
+ info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
+#endif // __ANDROID__
+ info.mcontext.mdlo = status->pr_reg[EF_LO];
+ info.mcontext.mdhi = status->pr_reg[EF_HI];
+ info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
+#else // __mips__
+ memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
+#endif // __mips__
+ if (first_thread) {
+ crash_thread_ = pid;
+ crash_signal_ = status->pr_info.si_signo;
+ crash_signal_code_ = status->pr_info.si_code;
+ }
+ first_thread = false;
+ threads_.push_back(pid);
+ thread_infos_.push_back(info);
+ break;
+ }
+ case NT_SIGINFO: {
+ if (description.length() != sizeof(siginfo_t)) {
+ fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n");
+ return false;
+ }
+
+ const siginfo_t* info =
+ reinterpret_cast<const siginfo_t*>(description.data());
+
+ // Set crash_address when si_addr is valid for the signal.
+ switch (info->si_signo) {
+ case MD_EXCEPTION_CODE_LIN_SIGBUS:
+ case MD_EXCEPTION_CODE_LIN_SIGFPE:
+ case MD_EXCEPTION_CODE_LIN_SIGILL:
+ case MD_EXCEPTION_CODE_LIN_SIGSEGV:
+ case MD_EXCEPTION_CODE_LIN_SIGSYS:
+ case MD_EXCEPTION_CODE_LIN_SIGTRAP:
+ crash_address_ = reinterpret_cast<uintptr_t>(info->si_addr);
+ break;
+ }
+
+ // Set crash_exception_info for common signals. Since exception info is
+ // unsigned, but some of these fields might be signed, we always cast.
+ switch (info->si_signo) {
+ case MD_EXCEPTION_CODE_LIN_SIGKILL:
+ set_crash_exception_info({
+ static_cast<uint64_t>(info->si_pid),
+ static_cast<uint64_t>(info->si_uid),
+ });
+ break;
+ case MD_EXCEPTION_CODE_LIN_SIGSYS:
+#ifdef si_syscall
+ set_crash_exception_info({
+ static_cast<uint64_t>(info->si_syscall),
+ static_cast<uint64_t>(info->si_arch),
+ });
+#endif
+ break;
+ }
+ break;
+ }
+#if defined(__i386) || defined(__x86_64)
+ case NT_FPREGSET: {
+ if (thread_infos_.empty())
+ return false;
+
+ ThreadInfo* info = &thread_infos_.back();
+ if (description.length() != sizeof(info->fpregs)) {
+ fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
+ return false;
+ }
+
+ memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
+ break;
+ }
+#endif
+#if defined(__i386)
+ case NT_PRXFPREG: {
+ if (thread_infos_.empty())
+ return false;
+
+ ThreadInfo* info = &thread_infos_.back();
+ if (description.length() != sizeof(info->fpxregs)) {
+ fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
+ return false;
+ }
+
+ memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
+ break;
+ }
+#endif
+ }
+ note = note.GetNextNote();
+ } while (note.IsValid());
+
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.h
new file mode 100644
index 0000000000..7805854a33
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper.h
@@ -0,0 +1,130 @@
+// 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.
+
+// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
+// class, which is derived from google_breakpad::LinuxDumper to extract
+// information from a crashed process via its core dump and proc files.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
+
+#include "linux/minidump_writer/linux_dumper.h"
+#include "common/linux/elf_core_dump.h"
+#include "common/linux/memory_mapped_file.h"
+
+namespace google_breakpad {
+
+class LinuxCoreDumper : public LinuxDumper {
+ public:
+ // Constructs a dumper for extracting information of a given process
+ // with a process ID of |pid| via its core dump file at |core_path| and
+ // its proc files at |procfs_path|. If |procfs_path| is a copy of
+ // /proc/<pid>, it should contain the following files:
+ // auxv, cmdline, environ, exe, maps, status
+ // See LinuxDumper for the purpose of |root_prefix|.
+ LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path,
+ const char* root_prefix = "");
+
+ // Implements LinuxDumper::BuildProcPath().
+ // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
+ // |path| is a character array of at least NAME_MAX bytes to return the
+ // result.|node| is the final node without any slashes. Return true on
+ // success.
+ //
+ // As this dumper performs a post-mortem dump and makes use of a copy
+ // of the proc files of the crashed process, this derived method does
+ // not actually make use of |pid| and always returns a subpath of
+ // |procfs_path_| regardless of whether |pid| corresponds to the main
+ // process or a thread of the process, i.e. assuming both the main process
+ // and its threads have the following proc files with the same content:
+ // auxv, cmdline, environ, exe, maps, status
+ virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
+
+ // Implements LinuxDumper::CopyFromProcess().
+ // Copies content of |length| bytes from a given process |child|,
+ // starting from |src|, into |dest|. This method extracts the content
+ // the core dump and fills |dest| with a sequence of marker bytes
+ // if the expected data is not found in the core dump. Returns true if
+ // the expected data is found in the core dump.
+ virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
+ size_t length);
+
+ // Implements LinuxDumper::GetThreadInfoByIndex().
+ // Reads information about the |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
+
+ // Implements LinuxDumper::GetThreadNameByIndex().
+ // Reads the name of the |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadNameByIndex(size_t index, char* info, size_t size);
+
+ // Implements LinuxDumper::IsPostMortem().
+ // Always returns true to indicate that this dumper performs a
+ // post-mortem dump of a crashed process via a core dump file.
+ virtual bool IsPostMortem() const;
+
+ // Implements LinuxDumper::ThreadsSuspend().
+ // As the dumper performs a post-mortem dump via a core dump file,
+ // there is no threads to suspend. This method does nothing and
+ // always returns true.
+ virtual bool ThreadsSuspend();
+
+ // Implements LinuxDumper::ThreadsResume().
+ // As the dumper performs a post-mortem dump via a core dump file,
+ // there is no threads to resume. This method does nothing and
+ // always returns true.
+ virtual bool ThreadsResume();
+
+ protected:
+ // Implements LinuxDumper::EnumerateThreads().
+ // Enumerates all threads of the given process into |threads_|.
+ virtual bool EnumerateThreads();
+
+ private:
+ // Path of the core dump file.
+ const char* core_path_;
+
+ // Path of the directory containing the proc files of the given process,
+ // which is usually a copy of /proc/<pid>.
+ const char* procfs_path_;
+
+ // Memory-mapped core dump file at |core_path_|.
+ MemoryMappedFile mapped_core_file_;
+
+ // Content of the core dump file.
+ ElfCoreDump core_;
+
+ // Thread info found in the core dump file.
+ wasteful_vector<ThreadInfo> thread_infos_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper_unittest.cc
new file mode 100644
index 0000000000..2e973167fd
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_core_dumper_unittest.cc
@@ -0,0 +1,192 @@
+// 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.
+
+// linux_core_dumper_unittest.cc:
+// Unit tests for google_breakpad::LinuxCoreDumoer.
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/minidump_writer/linux_core_dumper.h"
+#include "common/linux/tests/crash_generator.h"
+#include "common/using_std_string.h"
+
+using namespace google_breakpad;
+
+TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
+ const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
+ const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"};
+
+ char path[PATH_MAX];
+ dumper.GetMappingAbsolutePath(mapping, path);
+
+ EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path);
+}
+
+TEST(LinuxCoreDumperTest, BuildProcPath) {
+ const pid_t pid = getpid();
+ const char procfs_path[] = "/procfs_copy";
+ LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
+
+ char maps_path[NAME_MAX] = "";
+ char maps_path_expected[NAME_MAX];
+ snprintf(maps_path_expected, sizeof(maps_path_expected),
+ "%s/maps", procfs_path);
+ EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
+ EXPECT_STREQ(maps_path_expected, maps_path);
+
+ EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
+
+ char long_node[NAME_MAX];
+ size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
+ memset(long_node, 'a', long_node_len);
+ long_node[long_node_len] = '\0';
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
+}
+
+TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
+ CrashGenerator crash_generator;
+ if (!crash_generator.HasDefaultCorePattern()) {
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
+ "is skipped due to non-default core pattern\n");
+ return;
+ }
+
+ const unsigned kNumOfThreads = 3;
+ const unsigned kCrashThread = 1;
+ const int kCrashSignal = SIGABRT;
+ pid_t child_pid;
+ ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
+ kCrashSignal, &child_pid));
+
+ const string core_file = crash_generator.GetCoreFilePath();
+ const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
+
+#if defined(__ANDROID__)
+ struct stat st;
+ if (stat(core_file.c_str(), &st) != 0) {
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
+ "skipped due to no core file being generated\n");
+ return;
+ }
+#endif
+
+ LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
+
+ EXPECT_TRUE(dumper.Init());
+
+ EXPECT_TRUE(dumper.IsPostMortem());
+
+ // These are no-ops and should always return true.
+ EXPECT_TRUE(dumper.ThreadsSuspend());
+ EXPECT_TRUE(dumper.ThreadsResume());
+
+ // Linux does not set the crash address with SIGABRT, so make sure it always
+ // sets the crash address to 0.
+ EXPECT_EQ(0U, dumper.crash_address());
+ EXPECT_EQ(kCrashSignal, dumper.crash_signal());
+ EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
+ dumper.crash_thread());
+
+#if defined(THREAD_SANITIZER)
+ EXPECT_GE(dumper.threads().size(), kNumOfThreads);
+#else
+ EXPECT_EQ(dumper.threads().size(), kNumOfThreads);
+#endif
+ for (unsigned i = 0; i < kNumOfThreads; ++i) {
+ ThreadInfo info;
+ EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
+ const void* stack;
+ size_t stack_len;
+ EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
+ EXPECT_EQ(getpid(), info.ppid);
+ }
+}
+
+TEST(LinuxCoreDumperTest, VerifyExceptionDetails) {
+ CrashGenerator crash_generator;
+ if (!crash_generator.HasDefaultCorePattern()) {
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
+ "is skipped due to non-default core pattern\n");
+ return;
+ }
+
+#ifndef si_syscall
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
+ "skipped due to old kernel/C library headers\n");
+ return;
+#endif
+
+ const unsigned kNumOfThreads = 2;
+ const unsigned kCrashThread = 1;
+ const int kCrashSignal = SIGSYS;
+ pid_t child_pid;
+ ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
+ kCrashSignal, &child_pid));
+
+ const string core_file = crash_generator.GetCoreFilePath();
+ const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
+
+#if defined(__ANDROID__)
+ struct stat st;
+ if (stat(core_file.c_str(), &st) != 0) {
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is "
+ "skipped due to no core file being generated\n");
+ return;
+ }
+#endif
+
+ LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
+
+ EXPECT_TRUE(dumper.Init());
+
+ EXPECT_TRUE(dumper.IsPostMortem());
+
+#if defined(__ANDROID__)
+ // TODO: For some reason, Android doesn't seem to pass this.
+ if (!dumper.crash_address()) {
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is "
+ "skipped due to missing signal details on Android\n");
+ return;
+ }
+#endif
+
+ // Check the exception details.
+ EXPECT_NE(0U, dumper.crash_address());
+ EXPECT_EQ(kCrashSignal, dumper.crash_signal());
+ EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
+ dumper.crash_thread());
+
+ // We check the length, but not the actual fields. We sent SIGSYS ourselves
+ // instead of the kernel, so the extended fields are garbage.
+ const std::vector<uint64_t> info(dumper.crash_exception_info());
+ EXPECT_EQ(2U, info.size());
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc
new file mode 100644
index 0000000000..3b400ce8ca
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc
@@ -0,0 +1,999 @@
+// 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.
+
+// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
+// See linux_dumper.h for details.
+
+// This code deals with the mechanics of getting information about a crashed
+// process. Since this code may run in a compromised address space, the same
+// rules apply as detailed at the top of minidump_writer.h: no libc calls and
+// use the alternative allocator.
+
+#include "linux/minidump_writer/linux_dumper.h"
+
+#include <assert.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "linux/minidump_writer/line_reader.h"
+#include "common/linux/elfutils.h"
+#include "common/linux/file_id.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/memory_mapped_file.h"
+#include "common/linux/safe_readlink.h"
+#include "google_breakpad/common/minidump_exception_linux.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+#if defined(__ANDROID__)
+
+// Android packed relocations definitions are not yet available from the
+// NDK header files, so we have to provide them manually here.
+#ifndef DT_LOOS
+#define DT_LOOS 0x6000000d
+#endif
+#ifndef DT_ANDROID_REL
+static const int DT_ANDROID_REL = DT_LOOS + 2;
+#endif
+#ifndef DT_ANDROID_RELA
+static const int DT_ANDROID_RELA = DT_LOOS + 4;
+#endif
+
+#endif // __ANDROID __
+
+static const char kMappedFileUnsafePrefix[] = "/dev/";
+static const char kDeletedSuffix[] = " (deleted)";
+static const char kReservedFlags[] = " ---p";
+static const char kMozillaIpcPrefix[] = "org.mozilla.ipc.";
+
+inline static bool IsMappedFileOpenUnsafe(
+ const google_breakpad::MappingInfo& mapping) {
+ // It is unsafe to attempt to open a mapped file that lives under /dev,
+ // because the semantics of the open may be driver-specific so we'd risk
+ // hanging the crash dumper. And a file in /dev/ almost certainly has no
+ // ELF file identifier anyways.
+ return my_strncmp(mapping.name,
+ kMappedFileUnsafePrefix,
+ sizeof(kMappedFileUnsafePrefix) - 1) == 0;
+}
+
+namespace google_breakpad {
+
+namespace {
+
+bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) {
+ return mapping.system_mapping_info.start_addr <= address &&
+ address < mapping.system_mapping_info.end_addr;
+}
+
+#if defined(__CHROMEOS__)
+
+// Recover memory mappings before writing dump on ChromeOS
+//
+// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
+// addresses. ChromeOS' hugepage implementation replaces some segments with
+// anonymous private pages, which is a restriction of current implementation
+// in Linux kernel at the time of writing. Thus, breakpad can no longer
+// symbolize addresses from those text segments replaced with hugepages.
+//
+// This postprocess tries to recover the mappings. Because hugepages are always
+// inserted in between some .text sections, it tries to infer the names and
+// offsets of the segments, by looking at segments immediately precede and
+// succeed them.
+//
+// For example, a text segment before hugepage optimization
+// 02001000-03002000 r-xp /opt/google/chrome/chrome
+//
+// can be broken into
+// 02001000-02200000 r-xp /opt/google/chrome/chrome
+// 02200000-03000000 r-xp
+// 03000000-03002000 r-xp /opt/google/chrome/chrome
+//
+// For more details, see:
+// crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization
+
+// Copied from CrOS' hugepage implementation, which is unlikely to change.
+// The hugepage size is 2M.
+const unsigned int kHpageShift = 21;
+const size_t kHpageSize = (1 << kHpageShift);
+const size_t kHpageMask = (~(kHpageSize - 1));
+
+// Find and merge anonymous r-xp segments with surrounding named segments.
+// There are two cases:
+
+// Case 1: curr, next
+// curr is anonymous
+// curr is r-xp
+// curr.size >= 2M
+// curr.size is a multiple of 2M.
+// next is backed by some file.
+// curr and next are contiguous.
+// offset(next) == sizeof(curr)
+void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) {
+ // Merged segments are marked with size = 0.
+ if (curr->size == 0 || next->size == 0)
+ return;
+
+ if (curr->size >= kHpageSize &&
+ curr->exec &&
+ (curr->size & kHpageMask) == curr->size &&
+ (curr->start_addr & kHpageMask) == curr->start_addr &&
+ curr->name[0] == '\0' &&
+ next->name[0] != '\0' &&
+ curr->start_addr + curr->size == next->start_addr &&
+ curr->size == next->offset) {
+
+ // matched
+ my_strlcpy(curr->name, next->name, NAME_MAX);
+ if (next->exec) {
+ // (curr, next)
+ curr->size += next->size;
+ next->size = 0;
+ }
+ }
+}
+
+// Case 2: prev, curr, next
+// curr is anonymous
+// curr is r-xp
+// curr.size >= 2M
+// curr.size is a multiple of 2M.
+// next and prev are backed by the same file.
+// prev, curr and next are contiguous.
+// offset(next) == offset(prev) + sizeof(prev) + sizeof(curr)
+void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr,
+ MappingInfo *next) {
+ // Merged segments are marked with size = 0.
+ if (prev->size == 0 || curr->size == 0 || next->size == 0)
+ return;
+
+ if (curr->size >= kHpageSize &&
+ curr->exec &&
+ (curr->size & kHpageMask) == curr->size &&
+ (curr->start_addr & kHpageMask) == curr->start_addr &&
+ curr->name[0] == '\0' &&
+ next->name[0] != '\0' &&
+ curr->start_addr + curr->size == next->start_addr &&
+ prev->start_addr + prev->size == curr->start_addr &&
+ my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
+ next->offset == prev->offset + prev->size + curr->size) {
+
+ // matched
+ my_strlcpy(curr->name, prev->name, NAME_MAX);
+ if (prev->exec) {
+ curr->offset = prev->offset;
+ curr->start_addr = prev->start_addr;
+ if (next->exec) {
+ // (prev, curr, next)
+ curr->size += prev->size + next->size;
+ prev->size = 0;
+ next->size = 0;
+ } else {
+ // (prev, curr), next
+ curr->size += prev->size;
+ prev->size = 0;
+ }
+ } else {
+ curr->offset = prev->offset + prev->size;
+ if (next->exec) {
+ // prev, (curr, next)
+ curr->size += next->size;
+ next->size = 0;
+ } else {
+ // prev, curr, next
+ }
+ }
+ }
+}
+
+// mappings_ is sorted excepted for the first entry.
+// This function tries to merge segemnts into the first entry,
+// then check for other sorted entries.
+// See LinuxDumper::EnumerateMappings().
+void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
+ // Find the candidate "next" to first segment, which is the only one that
+ // could be out-of-order.
+ size_t l = 1;
+ size_t r = mappings.size();
+ size_t next = mappings.size();
+ while (l < r) {
+ int m = (l + r) / 2;
+ if (mappings[m]->start_addr > mappings[0]->start_addr)
+ r = next = m;
+ else
+ l = m + 1;
+ }
+
+ // Shows the range that contains the entry point is
+ // [first_start_addr, first_end_addr)
+ size_t first_start_addr = mappings[0]->start_addr;
+ size_t first_end_addr = mappings[0]->start_addr + mappings[0]->size;
+
+ // Put the out-of-order segment in order.
+ std::rotate(mappings.begin(), mappings.begin() + 1, mappings.begin() + next);
+
+ // Iterate through normal, sorted cases.
+ // Normal case 1.
+ for (size_t i = 0; i < mappings.size() - 1; i++)
+ TryRecoverMappings(mappings[i], mappings[i + 1]);
+
+ // Normal case 2.
+ for (size_t i = 0; i < mappings.size() - 2; i++)
+ TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
+
+ // Collect merged (size == 0) segments.
+ size_t f, e;
+ for (f = e = 0; e < mappings.size(); e++)
+ if (mappings[e]->size > 0)
+ mappings[f++] = mappings[e];
+ mappings.resize(f);
+
+ // The entry point is in the first mapping. We want to find the location
+ // of the entry point after merging segment. To do this, we want to find
+ // the mapping that covers the first mapping from the original mapping list.
+ // If the mapping is not in the beginning, we move it to the begining via
+ // a right rotate by using reverse iterators.
+ for (l = 0; l < mappings.size(); l++) {
+ if (mappings[l]->start_addr <= first_start_addr
+ && (mappings[l]->start_addr + mappings[l]->size >= first_end_addr))
+ break;
+ }
+ if (l > 0) {
+ r = mappings.size();
+ std::rotate(mappings.rbegin() + r - l - 1, mappings.rbegin() + r - l,
+ mappings.rend());
+ }
+}
+
+#endif // __CHROMEOS__
+
+} // namespace
+
+// All interesting auvx entry types are below AT_SYSINFO_EHDR
+#define AT_MAX AT_SYSINFO_EHDR
+
+LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
+ : pid_(pid),
+ root_prefix_(root_prefix),
+ crash_address_(0),
+ crash_signal_(0),
+ crash_signal_code_(0),
+ crash_thread_(pid),
+ threads_(&allocator_, 8),
+ mappings_(&allocator_),
+ auxv_(&allocator_, AT_MAX + 1) {
+ assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX);
+ // The passed-in size to the constructor (above) is only a hint.
+ // Must call .resize() to do actual initialization of the elements.
+ auxv_.resize(AT_MAX + 1);
+}
+
+LinuxDumper::~LinuxDumper() {
+}
+
+bool LinuxDumper::Init() {
+ return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
+}
+
+bool LinuxDumper::LateInit() {
+#if defined(__ANDROID__)
+ LatePostprocessMappings();
+#endif
+
+#if defined(__CHROMEOS__)
+ CrOSPostProcessMappings(mappings_);
+#endif
+
+ return true;
+}
+
+bool
+LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
+ bool member,
+ unsigned int mapping_id,
+ wasteful_vector<uint8_t>& identifier) {
+ assert(!member || mapping_id < mappings_.size());
+ if (IsMappedFileOpenUnsafe(mapping))
+ return false;
+
+ // Special-case linux-gate because it's not a real file.
+ if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
+ void* linux_gate = NULL;
+ if (pid_ == sys_getpid()) {
+ linux_gate = reinterpret_cast<void*>(mapping.start_addr);
+ } else {
+ linux_gate = allocator_.Alloc(mapping.size);
+ CopyFromProcess(linux_gate, pid_,
+ reinterpret_cast<const void*>(mapping.start_addr),
+ mapping.size);
+ }
+ return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
+ }
+
+ char filename[PATH_MAX];
+ if (!GetMappingAbsolutePath(mapping, filename))
+ return false;
+ bool filename_modified = HandleDeletedFileInMapping(filename);
+
+ MemoryMappedFile mapped_file(filename, mapping.offset);
+ if (!mapped_file.data() || mapped_file.size() < SELFMAG)
+ return false;
+
+ bool success =
+ FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
+ if (success && member && filename_modified) {
+ mappings_[mapping_id]->name[my_strlen(mapping.name) -
+ sizeof(kDeletedSuffix) + 1] = '\0';
+ }
+
+ return success;
+}
+
+void LinuxDumper::SetCrashInfoFromSigInfo(const siginfo_t& siginfo) {
+ set_crash_address(reinterpret_cast<uintptr_t>(siginfo.si_addr));
+ set_crash_signal(siginfo.si_signo);
+ set_crash_signal_code(siginfo.si_code);
+}
+
+const char* LinuxDumper::GetCrashSignalString() const {
+ switch (static_cast<unsigned int>(crash_signal_)) {
+ case MD_EXCEPTION_CODE_LIN_SIGHUP:
+ return "SIGHUP";
+ case MD_EXCEPTION_CODE_LIN_SIGINT:
+ return "SIGINT";
+ case MD_EXCEPTION_CODE_LIN_SIGQUIT:
+ return "SIGQUIT";
+ case MD_EXCEPTION_CODE_LIN_SIGILL:
+ return "SIGILL";
+ case MD_EXCEPTION_CODE_LIN_SIGTRAP:
+ return "SIGTRAP";
+ case MD_EXCEPTION_CODE_LIN_SIGABRT:
+ return "SIGABRT";
+ case MD_EXCEPTION_CODE_LIN_SIGBUS:
+ return "SIGBUS";
+ case MD_EXCEPTION_CODE_LIN_SIGFPE:
+ return "SIGFPE";
+ case MD_EXCEPTION_CODE_LIN_SIGKILL:
+ return "SIGKILL";
+ case MD_EXCEPTION_CODE_LIN_SIGUSR1:
+ return "SIGUSR1";
+ case MD_EXCEPTION_CODE_LIN_SIGSEGV:
+ return "SIGSEGV";
+ case MD_EXCEPTION_CODE_LIN_SIGUSR2:
+ return "SIGUSR2";
+ case MD_EXCEPTION_CODE_LIN_SIGPIPE:
+ return "SIGPIPE";
+ case MD_EXCEPTION_CODE_LIN_SIGALRM:
+ return "SIGALRM";
+ case MD_EXCEPTION_CODE_LIN_SIGTERM:
+ return "SIGTERM";
+ case MD_EXCEPTION_CODE_LIN_SIGSTKFLT:
+ return "SIGSTKFLT";
+ case MD_EXCEPTION_CODE_LIN_SIGCHLD:
+ return "SIGCHLD";
+ case MD_EXCEPTION_CODE_LIN_SIGCONT:
+ return "SIGCONT";
+ case MD_EXCEPTION_CODE_LIN_SIGSTOP:
+ return "SIGSTOP";
+ case MD_EXCEPTION_CODE_LIN_SIGTSTP:
+ return "SIGTSTP";
+ case MD_EXCEPTION_CODE_LIN_SIGTTIN:
+ return "SIGTTIN";
+ case MD_EXCEPTION_CODE_LIN_SIGTTOU:
+ return "SIGTTOU";
+ case MD_EXCEPTION_CODE_LIN_SIGURG:
+ return "SIGURG";
+ case MD_EXCEPTION_CODE_LIN_SIGXCPU:
+ return "SIGXCPU";
+ case MD_EXCEPTION_CODE_LIN_SIGXFSZ:
+ return "SIGXFSZ";
+ case MD_EXCEPTION_CODE_LIN_SIGVTALRM:
+ return "SIGVTALRM";
+ case MD_EXCEPTION_CODE_LIN_SIGPROF:
+ return "SIGPROF";
+ case MD_EXCEPTION_CODE_LIN_SIGWINCH:
+ return "SIGWINCH";
+ case MD_EXCEPTION_CODE_LIN_SIGIO:
+ return "SIGIO";
+ case MD_EXCEPTION_CODE_LIN_SIGPWR:
+ return "SIGPWR";
+ case MD_EXCEPTION_CODE_LIN_SIGSYS:
+ return "SIGSYS";
+ case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
+ return "DUMP_REQUESTED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
+ char path[PATH_MAX]) const {
+ return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
+ my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX;
+}
+
+namespace {
+// Find the shared object name (SONAME) by examining the ELF information
+// for |mapping|. If the SONAME is found copy it into the passed buffer
+// |soname| and return true. The size of the buffer is |soname_size|.
+// The SONAME will be truncated if it is too long to fit in the buffer.
+bool ElfFileSoName(const LinuxDumper& dumper,
+ const MappingInfo& mapping, char* soname, size_t soname_size) {
+ if (IsMappedFileOpenUnsafe(mapping)) {
+ // Not safe
+ return false;
+ }
+
+ char filename[PATH_MAX];
+ if (!dumper.GetMappingAbsolutePath(mapping, filename))
+ return false;
+
+ MemoryMappedFile mapped_file(filename, mapping.offset);
+ if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
+ // mmap failed
+ return false;
+ }
+
+ return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
+}
+
+} // namespace
+
+
+void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
+ char* file_path,
+ size_t file_path_size,
+ char* file_name,
+ size_t file_name_size) {
+ my_strlcpy(file_path, mapping.name, file_path_size);
+
+ // Tools such as minidump_stackwalk use the name of the module to look up
+ // symbols produced by dump_syms. dump_syms will prefer to use a module's
+ // DT_SONAME as the module name, if one exists, and will fall back to the
+ // filesystem name of the module.
+
+ // Just use the filesystem name if no SONAME is present.
+ if (!ElfFileSoName(*this, mapping, file_name, file_name_size)) {
+ // file_path := /path/to/libname.so
+ // file_name := libname.so
+ const char* basename = my_strrchr(file_path, '/');
+ basename = basename == NULL ? file_path : (basename + 1);
+ my_strlcpy(file_name, basename, file_name_size);
+ return;
+ }
+
+ if (mapping.exec && mapping.offset != 0) {
+ // If an executable is mapped from a non-zero offset, this is likely because
+ // the executable was loaded directly from inside an archive file (e.g., an
+ // apk on Android).
+ // In this case, we append the file_name to the mapped archive path:
+ // file_name := libname.so
+ // file_path := /path/to/ARCHIVE.APK/libname.so
+ if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
+ my_strlcat(file_path, "/", file_path_size);
+ my_strlcat(file_path, file_name, file_path_size);
+ }
+ } else {
+ // Otherwise, replace the basename with the SONAME.
+ char* basename = const_cast<char*>(my_strrchr(file_path, '/'));
+ if (basename) {
+ my_strlcpy(basename + 1, file_name,
+ file_path_size - my_strlen(file_path) +
+ my_strlen(basename + 1));
+ } else {
+ my_strlcpy(file_path, file_name, file_path_size);
+ }
+ }
+}
+
+bool LinuxDumper::ReadAuxv() {
+ char auxv_path[NAME_MAX];
+ if (!BuildProcPath(auxv_path, pid_, "auxv")) {
+ return false;
+ }
+
+ int fd = sys_open(auxv_path, O_RDONLY, 0);
+ if (fd < 0) {
+ return false;
+ }
+
+ elf_aux_entry one_aux_entry;
+ bool res = false;
+ while (sys_read(fd,
+ &one_aux_entry,
+ sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
+ one_aux_entry.a_type != AT_NULL) {
+ if (one_aux_entry.a_type <= AT_MAX) {
+ auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
+ res = true;
+ }
+ }
+ sys_close(fd);
+ return res;
+}
+
+bool LinuxDumper::IsIPCSharedMemorySegment(const char* name) {
+ if (my_strstr(name, kMozillaIpcPrefix) &&
+ my_strstr(name, kDeletedSuffix)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool LinuxDumper::EnumerateMappings() {
+ char maps_path[NAME_MAX];
+ if (!BuildProcPath(maps_path, pid_, "maps"))
+ return false;
+
+ // linux_gate_loc is the beginning of the kernel's mapping of
+ // linux-gate.so in the process. It doesn't actually show up in the
+ // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
+ // aux vector entry, which gives the information necessary to special
+ // case its entry when creating the list of mappings.
+ // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
+ // information.
+ const void* linux_gate_loc =
+ reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
+ // Although the initial executable is usually the first mapping, it's not
+ // guaranteed (see http://crosbug.com/25355); therefore, try to use the
+ // actual entry point to find the mapping.
+ const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
+
+ const int fd = sys_open(maps_path, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+ LineReader* const line_reader = new(allocator_) LineReader(fd);
+
+ const char* line;
+ unsigned line_len;
+ while (line_reader->GetNextLine(&line, &line_len)) {
+ uintptr_t start_addr, end_addr, offset;
+
+ const char* i1 = my_read_hex_ptr(&start_addr, line);
+ if (*i1 == '-') {
+ const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
+ if (*i2 == ' ') {
+ bool exec = (*(i2 + 3) == 'x');
+ const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
+ if (*i3 == ' ') {
+ const char* name = NULL;
+ // Only copy name if the name is a valid path name, or if
+ // it's the VDSO image.
+ if (((name = my_strchr(line, '/')) == NULL) &&
+ linux_gate_loc &&
+ reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
+ name = kLinuxGateLibraryName;
+ offset = 0;
+ }
+ // Skip shared memory segments used for IPC
+ if (name && IsIPCSharedMemorySegment(name)) {
+ line_reader->PopLine(line_len);
+ continue;
+ }
+ // Merge adjacent mappings into one module, assuming they're a single
+ // library mapped by the dynamic linker.
+ if (name && !mappings_.empty()) {
+ MappingInfo* module = mappings_.back();
+ if ((start_addr == module->start_addr + module->size) &&
+ (my_strlen(name) == my_strlen(module->name)) &&
+ (my_strncmp(name, module->name, my_strlen(name)) == 0)) {
+ module->system_mapping_info.end_addr = end_addr;
+ module->size = end_addr - module->start_addr;
+ module->exec |= exec;
+ line_reader->PopLine(line_len);
+ continue;
+ }
+ }
+ // Also merge mappings that result from address ranges that the
+ // linker reserved but which a loaded library did not use. These
+ // appear as an anonymous private mapping with no access flags set
+ // and which directly follow an executable mapping.
+ if (!name && !mappings_.empty()) {
+ MappingInfo* module = mappings_.back();
+ uintptr_t module_end_addr = module->start_addr + module->size;
+ if ((start_addr == module_end_addr) &&
+ module->exec &&
+ module->name[0] == '/' &&
+ ((offset == 0) || (offset == module_end_addr)) &&
+ my_strncmp(i2, kReservedFlags,
+ sizeof(kReservedFlags) - 1) == 0) {
+ module->size = end_addr - module->start_addr;
+ line_reader->PopLine(line_len);
+ continue;
+ }
+ }
+ MappingInfo* const module = new(allocator_) MappingInfo;
+ mappings_.push_back(module);
+ my_memset(module, 0, sizeof(MappingInfo));
+ module->system_mapping_info.start_addr = start_addr;
+ module->system_mapping_info.end_addr = end_addr;
+ module->start_addr = start_addr;
+ module->size = end_addr - start_addr;
+ module->offset = offset;
+ module->exec = exec;
+ if (name != NULL) {
+ const unsigned l = my_strlen(name);
+ if (l < sizeof(module->name))
+ my_memcpy(module->name, name, l);
+ }
+ }
+ }
+ }
+ line_reader->PopLine(line_len);
+ }
+
+ if (entry_point_loc) {
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ MappingInfo* module = mappings_[i];
+
+ // If this module contains the entry-point, and it's not already the first
+ // one, then we need to make it be first. This is because the minidump
+ // format assumes the first module is the one that corresponds to the main
+ // executable (as codified in
+ // processor/minidump.cc:MinidumpModuleList::GetMainModule()).
+ if ((entry_point_loc >= reinterpret_cast<void*>(module->start_addr)) &&
+ (entry_point_loc <
+ reinterpret_cast<void*>(module->start_addr + module->size))) {
+ for (size_t j = i; j > 0; j--)
+ mappings_[j] = mappings_[j - 1];
+ mappings_[0] = module;
+ break;
+ }
+ }
+ }
+
+ sys_close(fd);
+
+ return !mappings_.empty();
+}
+
+#if defined(__ANDROID__)
+
+bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
+ CopyFromProcess(ehdr, pid_,
+ reinterpret_cast<const void*>(start_addr),
+ sizeof(*ehdr));
+ return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+}
+
+void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
+ uintptr_t start_addr,
+ uintptr_t* min_vaddr_ptr,
+ uintptr_t* dyn_vaddr_ptr,
+ size_t* dyn_count_ptr) {
+ uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
+
+ const uintptr_t max_addr = UINTPTR_MAX;
+ uintptr_t min_vaddr = max_addr;
+ uintptr_t dyn_vaddr = 0;
+ size_t dyn_count = 0;
+
+ for (size_t i = 0; i < ehdr->e_phnum; ++i) {
+ ElfW(Phdr) phdr;
+ CopyFromProcess(&phdr, pid_,
+ reinterpret_cast<const void*>(phdr_addr),
+ sizeof(phdr));
+ if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) {
+ min_vaddr = phdr.p_vaddr;
+ }
+ if (phdr.p_type == PT_DYNAMIC) {
+ dyn_vaddr = phdr.p_vaddr;
+ dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn));
+ }
+ phdr_addr += sizeof(phdr);
+ }
+
+ *min_vaddr_ptr = min_vaddr;
+ *dyn_vaddr_ptr = dyn_vaddr;
+ *dyn_count_ptr = dyn_count;
+}
+
+bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
+ uintptr_t dyn_vaddr,
+ size_t dyn_count) {
+ uintptr_t dyn_addr = load_bias + dyn_vaddr;
+ for (size_t i = 0; i < dyn_count; ++i) {
+ ElfW(Dyn) dyn;
+ CopyFromProcess(&dyn, pid_,
+ reinterpret_cast<const void*>(dyn_addr),
+ sizeof(dyn));
+ if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) {
+ return true;
+ }
+ dyn_addr += sizeof(dyn);
+ }
+ return false;
+}
+
+uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr,
+ uintptr_t start_addr) {
+ uintptr_t min_vaddr = 0;
+ uintptr_t dyn_vaddr = 0;
+ size_t dyn_count = 0;
+ ParseLoadedElfProgramHeaders(ehdr, start_addr,
+ &min_vaddr, &dyn_vaddr, &dyn_count);
+ // If |min_vaddr| is non-zero and we find Android packed relocation tags,
+ // return the effective load bias.
+ if (min_vaddr != 0) {
+ const uintptr_t load_bias = start_addr - min_vaddr;
+ if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) {
+ return load_bias;
+ }
+ }
+ // Either |min_vaddr| is zero, or it is non-zero but we did not find the
+ // expected Android packed relocations tags.
+ return start_addr;
+}
+
+void LinuxDumper::LatePostprocessMappings() {
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ // Only consider exec mappings that indicate a file path was mapped, and
+ // where the ELF header indicates a mapped shared library.
+ MappingInfo* mapping = mappings_[i];
+ if (!(mapping->exec && mapping->name[0] == '/')) {
+ continue;
+ }
+ ElfW(Ehdr) ehdr;
+ if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
+ continue;
+ }
+ if (ehdr.e_type == ET_DYN) {
+ // Compute the effective load bias for this mapped library, and update
+ // the mapping to hold that rather than |start_addr|, at the same time
+ // adjusting |size| to account for the change in |start_addr|. Where
+ // the library does not contain Android packed relocations,
+ // GetEffectiveLoadBias() returns |start_addr| and the mapping entry
+ // is not changed.
+ const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr,
+ mapping->start_addr);
+ mapping->size += mapping->start_addr - load_bias;
+ mapping->start_addr = load_bias;
+ }
+ }
+}
+
+#endif // __ANDROID__
+
+// Get information about the stack, given the stack pointer. We don't try to
+// walk the stack since we might not have all the information needed to do
+// unwind. So we just grab, up to, 32k of stack.
+bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
+ uintptr_t int_stack_pointer) {
+ // Move the stack pointer to the bottom of the page that it's in.
+ const uintptr_t page_size = getpagesize();
+
+ uint8_t* const stack_pointer =
+ reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
+
+ // The number of bytes of stack which we try to capture.
+ static const ptrdiff_t kStackToCapture = 32 * 1024;
+
+ const MappingInfo* mapping = FindMapping(stack_pointer);
+ if (!mapping)
+ return false;
+ const ptrdiff_t offset = stack_pointer -
+ reinterpret_cast<uint8_t*>(mapping->start_addr);
+ const ptrdiff_t distance_to_end =
+ static_cast<ptrdiff_t>(mapping->size) - offset;
+ *stack_len = distance_to_end > kStackToCapture ?
+ kStackToCapture : distance_to_end;
+ *stack = stack_pointer;
+ return true;
+}
+
+void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
+ uintptr_t stack_pointer,
+ uintptr_t sp_offset) {
+ // We optimize the search for containing mappings in three ways:
+ // 1) We expect that pointers into the stack mapping will be common, so
+ // we cache that address range.
+ // 2) The last referenced mapping is a reasonable predictor for the next
+ // referenced mapping, so we test that first.
+ // 3) We precompute a bitfield based upon bits 32:32-n of the start and
+ // stop addresses, and use that to short circuit any values that can
+ // not be pointers. (n=11)
+ const uintptr_t defaced =
+#if defined(__LP64__)
+ 0x0defaced0defaced;
+#else
+ 0x0defaced;
+#endif
+ // the bitfield length is 2^test_bits long.
+ const unsigned int test_bits = 11;
+ // byte length of the corresponding array.
+ const unsigned int array_size = 1 << (test_bits - 3);
+ const unsigned int array_mask = array_size - 1;
+ // The amount to right shift pointers by. This captures the top bits
+ // on 32 bit architectures. On 64 bit architectures this would be
+ // uninformative so we take the same range of bits.
+ const unsigned int shift = 32 - 11;
+ const MappingInfo* last_hit_mapping = nullptr;
+ const MappingInfo* hit_mapping = nullptr;
+ const MappingInfo* stack_mapping = FindMappingNoBias(stack_pointer);
+ // The magnitude below which integers are considered to be to be
+ // 'small', and not constitute a PII risk. These are included to
+ // avoid eliding useful register values.
+ const ssize_t small_int_magnitude = 4096;
+
+ char could_hit_mapping[array_size];
+ my_memset(could_hit_mapping, 0, array_size);
+
+ // Initialize the bitfield such that if the (pointer >> shift)'th
+ // bit, modulo the bitfield size, is not set then there does not
+ // exist a mapping in mappings_ that would contain that pointer.
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ if (!mappings_[i]->exec) continue;
+ // For each mapping, work out the (unmodulo'ed) range of bits to
+ // set.
+ uintptr_t start = mappings_[i]->start_addr;
+ uintptr_t end = start + mappings_[i]->size;
+ start >>= shift;
+ end >>= shift;
+ for (size_t bit = start; bit <= end; ++bit) {
+ // Set each bit in the range, applying the modulus.
+ could_hit_mapping[(bit >> 3) & array_mask] |= 1 << (bit & 7);
+ }
+ }
+
+ // Zero memory that is below the current stack pointer.
+ const uintptr_t offset =
+ (sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
+ if (offset) {
+ my_memset(stack_copy, 0, offset);
+ }
+
+ // Apply sanitization to each complete pointer-aligned word in the
+ // stack.
+ uint8_t* sp;
+ for (sp = stack_copy + offset;
+ sp <= stack_copy + stack_len - sizeof(uintptr_t);
+ sp += sizeof(uintptr_t)) {
+ uintptr_t addr;
+ my_memcpy(&addr, sp, sizeof(uintptr_t));
+ if (static_cast<intptr_t>(addr) <= small_int_magnitude &&
+ static_cast<intptr_t>(addr) >= -small_int_magnitude) {
+ continue;
+ }
+ if (stack_mapping && MappingContainsAddress(*stack_mapping, addr)) {
+ continue;
+ }
+ if (last_hit_mapping && MappingContainsAddress(*last_hit_mapping, addr)) {
+ continue;
+ }
+ uintptr_t test = addr >> shift;
+ if (could_hit_mapping[(test >> 3) & array_mask] & (1 << (test & 7)) &&
+ (hit_mapping = FindMappingNoBias(addr)) != nullptr &&
+ hit_mapping->exec) {
+ last_hit_mapping = hit_mapping;
+ continue;
+ }
+ my_memcpy(sp, &defaced, sizeof(uintptr_t));
+ }
+ // Zero any partial word at the top of the stack, if alignment is
+ // such that that is required.
+ if (sp < stack_copy + stack_len) {
+ my_memset(sp, 0, stack_copy + stack_len - sp);
+ }
+}
+
+bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy,
+ size_t stack_len,
+ uintptr_t sp_offset,
+ const MappingInfo& mapping) {
+ // Loop over all stack words that would have been on the stack in
+ // the target process (i.e. are word aligned, and at addresses >=
+ // the stack pointer). Regardless of the alignment of |stack_copy|,
+ // the memory starting at |stack_copy| + |offset| represents an
+ // aligned word in the target process.
+ const uintptr_t low_addr = mapping.system_mapping_info.start_addr;
+ const uintptr_t high_addr = mapping.system_mapping_info.end_addr;
+ const uintptr_t offset =
+ (sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
+
+ for (const uint8_t* sp = stack_copy + offset;
+ sp <= stack_copy + stack_len - sizeof(uintptr_t);
+ sp += sizeof(uintptr_t)) {
+ uintptr_t addr;
+ my_memcpy(&addr, sp, sizeof(uintptr_t));
+ if (low_addr <= addr && addr <= high_addr)
+ return true;
+ }
+ return false;
+}
+
+// Find the mapping which the given memory address falls in.
+const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
+ const uintptr_t addr = (uintptr_t) address;
+
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
+ if (addr >= start && addr - start < mappings_[i]->size)
+ return mappings_[i];
+ }
+
+ return NULL;
+}
+
+// Find the mapping which the given memory address falls in. Uses the
+// unadjusted mapping address range from the kernel, rather than the
+// biased range.
+const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const {
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ if (address >= mappings_[i]->system_mapping_info.start_addr &&
+ address < mappings_[i]->system_mapping_info.end_addr) {
+ return mappings_[i];
+ }
+ }
+ return NULL;
+}
+
+bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
+ static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
+
+ // Check for ' (deleted)' in |path|.
+ // |path| has to be at least as long as "/x (deleted)".
+ const size_t path_len = my_strlen(path);
+ if (path_len < kDeletedSuffixLen + 2)
+ return false;
+ if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
+ kDeletedSuffixLen) != 0) {
+ return false;
+ }
+
+ // Check |path| against the /proc/pid/exe 'symlink'.
+ char exe_link[NAME_MAX];
+ if (!BuildProcPath(exe_link, pid_, "exe"))
+ return false;
+ MappingInfo new_mapping = {0};
+ if (!SafeReadLink(exe_link, new_mapping.name))
+ return false;
+ char new_path[PATH_MAX];
+ if (!GetMappingAbsolutePath(new_mapping, new_path))
+ return false;
+ if (my_strcmp(path, new_path) != 0)
+ return false;
+
+ // Check to see if someone actually named their executable 'foo (deleted)'.
+ struct kernel_stat exe_stat;
+ struct kernel_stat new_path_stat;
+ if (sys_stat(exe_link, &exe_stat) == 0 &&
+ sys_stat(new_path, &new_path_stat) == 0 &&
+ exe_stat.st_dev == new_path_stat.st_dev &&
+ exe_stat.st_ino == new_path_stat.st_ino) {
+ return false;
+ }
+
+ my_memcpy(path, exe_link, NAME_MAX);
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h
new file mode 100644
index 0000000000..7155524ffc
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h
@@ -0,0 +1,333 @@
+// 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.
+
+// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
+// is a base class for extracting information of a crashed process. It
+// was originally a complete implementation using the ptrace API, but
+// has been refactored to allow derived implementations supporting both
+// ptrace and core dump. A portion of the original implementation is now
+// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
+// details).
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
+
+#include <assert.h>
+#include <elf.h>
+#if defined(__ANDROID__)
+#include <link.h>
+#endif
+#include <linux/limits.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include <vector>
+
+#include "linux/dump_writer_common/mapping_info.h"
+#include "linux/dump_writer_common/thread_info.h"
+#include "common/linux/file_id.h"
+#include "common/memory_allocator.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
+#if defined(__i386) || defined(__ARM_EABI__) || \
+ (defined(__mips__) && _MIPS_SIM == _ABIO32)
+typedef Elf32_auxv_t elf_aux_entry;
+#elif defined(__x86_64) || defined(__aarch64__) || \
+ (defined(__mips__) && _MIPS_SIM != _ABIO32)
+typedef Elf64_auxv_t elf_aux_entry;
+#endif
+
+typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
+
+// When we find the VDSO mapping in the process's address space, this
+// is the name we use for it when writing it to the minidump.
+// This should always be less than NAME_MAX!
+const char kLinuxGateLibraryName[] = "linux-gate.so";
+
+class LinuxDumper {
+ public:
+ // The |root_prefix| is prepended to mapping paths before opening them, which
+ // is useful if the crash originates from a chroot.
+ explicit LinuxDumper(pid_t pid, const char* root_prefix = "");
+
+ virtual ~LinuxDumper();
+
+ // Parse the data for |threads| and |mappings|.
+ virtual bool Init();
+
+ // Take any actions that could not be taken in Init(). LateInit() is
+ // called after all other caller's initialization is complete, and in
+ // particular after it has called ThreadsSuspend(), so that ptrace is
+ // available.
+ virtual bool LateInit();
+
+ // Return true if the dumper performs a post-mortem dump.
+ virtual bool IsPostMortem() const = 0;
+
+ // Suspend/resume all threads in the given process.
+ virtual bool ThreadsSuspend() = 0;
+ virtual bool ThreadsResume() = 0;
+
+ // Read information about the |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
+
+ // Read the name ofthe |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadNameByIndex(size_t index, char* name, size_t size) = 0;
+
+ size_t GetMainThreadIndex() const {
+ for (size_t i = 0; i < threads_.size(); ++i) {
+ if (threads_[i] == pid_) return i;
+ }
+ return -1u;
+ }
+
+ // These are only valid after a call to |Init|.
+ const wasteful_vector<pid_t> &threads() { return threads_; }
+ const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
+ const MappingInfo* FindMapping(const void* address) const;
+ // Find the mapping which the given memory address falls in. Unlike
+ // FindMapping, this method uses the unadjusted mapping address
+ // ranges from the kernel, rather than the ranges that have had the
+ // load bias applied.
+ const MappingInfo* FindMappingNoBias(uintptr_t address) const;
+ const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
+
+ // Find a block of memory to take as the stack given the top of stack pointer.
+ // stack: (output) the lowest address in the memory area
+ // stack_len: (output) the length of the memory area
+ // stack_top: the current top of the stack
+ bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
+
+ // Sanitize a copy of the stack by overwriting words that are not
+ // pointers with a sentinel (0x0defaced).
+ // stack_copy: a copy of the stack to sanitize. |stack_copy| might
+ // not be word aligned, but it represents word aligned
+ // data copied from another location.
+ // stack_len: the length of the allocation pointed to by |stack_copy|.
+ // stack_pointer: the address of the stack pointer (used to locate
+ // the stack mapping, as an optimization).
+ // sp_offset: the offset relative to stack_copy that reflects the
+ // current value of the stack pointer.
+ void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
+ uintptr_t stack_pointer, uintptr_t sp_offset);
+
+ // Test whether |stack_copy| contains a pointer-aligned word that
+ // could be an address within a given mapping.
+ // stack_copy: a copy of the stack to check. |stack_copy| might
+ // not be word aligned, but it represents word aligned
+ // data copied from another location.
+ // stack_len: the length of the allocation pointed to by |stack_copy|.
+ // sp_offset: the offset relative to stack_copy that reflects the
+ // current value of the stack pointer.
+ // mapping: the mapping against which to test stack words.
+ bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len,
+ uintptr_t sp_offset,
+ const MappingInfo& mapping);
+
+ PageAllocator* allocator() { return &allocator_; }
+
+ // Copy content of |length| bytes from a given process |child|,
+ // starting from |src|, into |dest|. Returns true on success.
+ virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
+ size_t length) = 0;
+
+ // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
+ // |path| is a character array of at least NAME_MAX bytes to return the
+ // result.|node| is the final node without any slashes. Returns true on
+ // success.
+ virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
+
+ // Generate a File ID from the .text section of a mapped entry.
+ // If not a member, mapping_id is ignored. This method can also manipulate the
+ // |mapping|.name to truncate "(deleted)" from the file name if necessary.
+ bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
+ bool member,
+ unsigned int mapping_id,
+ wasteful_vector<uint8_t>& identifier);
+
+ void SetCrashInfoFromSigInfo(const siginfo_t& siginfo);
+
+ uintptr_t crash_address() const { return crash_address_; }
+ void set_crash_address(uintptr_t crash_address) {
+ crash_address_ = crash_address;
+ }
+
+ int crash_signal() const { return crash_signal_; }
+ void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
+ const char* GetCrashSignalString() const;
+
+ void set_crash_signal_code(int code) { crash_signal_code_ = code; }
+ int crash_signal_code() const { return crash_signal_code_; }
+
+ void set_crash_exception_info(const std::vector<uint64_t>& exception_info) {
+ assert(exception_info.size() <= MD_EXCEPTION_MAXIMUM_PARAMETERS);
+ crash_exception_info_ = exception_info;
+ }
+ const std::vector<uint64_t>& crash_exception_info() const {
+ return crash_exception_info_;
+ }
+
+ pid_t pid() const { return pid_; }
+ pid_t crash_thread() const { return crash_thread_; }
+ void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
+
+ // Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and
+ // returns true unless the string is too long.
+ bool GetMappingAbsolutePath(const MappingInfo& mapping,
+ char path[PATH_MAX]) const;
+
+ // Extracts the effective path and file name of from |mapping|. In most cases
+ // the effective name/path are just the mapping's path and basename. In some
+ // other cases, however, a library can be mapped from an archive (e.g., when
+ // loading .so libs from an apk on Android) and this method is able to
+ // reconstruct the original file name.
+ void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
+ char* file_path,
+ size_t file_path_size,
+ char* file_name,
+ size_t file_name_size);
+
+ protected:
+ bool ReadAuxv();
+
+ virtual bool EnumerateMappings();
+
+ virtual bool EnumerateThreads() = 0;
+
+ // For the case where a running program has been deleted, it'll show up in
+ // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
+ // see if '/path/to/program (deleted)' matches /proc/pid/exe and return
+ // /proc/pid/exe in |path| so ELF identifier generation works correctly. This
+ // also checks to see if '/path/to/program (deleted)' exists, so it does not
+ // get fooled by a poorly named binary.
+ // For programs that don't end with ' (deleted)', this is a no-op.
+ // This assumes |path| is a buffer with length NAME_MAX.
+ // Returns true if |path| is modified.
+ bool HandleDeletedFileInMapping(char* path) const;
+
+ // ID of the crashed process.
+ const pid_t pid_;
+
+ // Path of the root directory to which mapping paths are relative.
+ const char* const root_prefix_;
+
+ // Virtual address at which the process crashed.
+ uintptr_t crash_address_;
+
+ // Signal that terminated the crashed process.
+ int crash_signal_;
+
+ // The code associated with |crash_signal_|.
+ int crash_signal_code_;
+
+ // The additional fields associated with |crash_signal_|.
+ std::vector<uint64_t> crash_exception_info_;
+
+ // ID of the crashed thread.
+ pid_t crash_thread_;
+
+ mutable PageAllocator allocator_;
+
+ // IDs of all the threads.
+ wasteful_vector<pid_t> threads_;
+
+ // Info from /proc/<pid>/maps.
+ wasteful_vector<MappingInfo*> mappings_;
+
+ // Info from /proc/<pid>/auxv
+ wasteful_vector<elf_aux_val_t> auxv_;
+
+private:
+ bool IsIPCSharedMemorySegment(const char* name);
+
+#if defined(__ANDROID__)
+ // Android M and later support packed ELF relocations in shared libraries.
+ // Packing relocations changes the vaddr of the LOAD segments, such that
+ // the effective load bias is no longer the same as the start address of
+ // the memory mapping containing the executable parts of the library. The
+ // packing is applied to the stripped library run on the target, but not to
+ // any other library, and in particular not to the library used to generate
+ // breakpad symbols. As a result, we need to adjust the |start_addr| for
+ // any mapping that results from a shared library that contains Android
+ // packed relocations, so that it properly represents the effective library
+ // load bias. The following functions support this adjustment.
+
+ // Check that a given mapping at |start_addr| is for an ELF shared library.
+ // If it is, place the ELF header in |ehdr| and return true.
+ // The first LOAD segment in an ELF shared library has offset zero, so the
+ // ELF file header is at the start of this map entry, and in already mapped
+ // memory.
+ bool GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr);
+
+ // For the ELF file mapped at |start_addr|, iterate ELF program headers to
+ // find the min vaddr of all program header LOAD segments, the vaddr for
+ // the DYNAMIC segment, and a count of DYNAMIC entries. Return values in
+ // |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|.
+ // The program header table is also in already mapped memory.
+ void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
+ uintptr_t start_addr,
+ uintptr_t* min_vaddr_ptr,
+ uintptr_t* dyn_vaddr_ptr,
+ size_t* dyn_count_ptr);
+
+ // Search the DYNAMIC tags for the ELF file with the given |load_bias|, and
+ // return true if the tags indicate that the file contains Android packed
+ // relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|.
+ bool HasAndroidPackedRelocations(uintptr_t load_bias,
+ uintptr_t dyn_vaddr,
+ size_t dyn_count);
+
+ // If the ELF file mapped at |start_addr| contained Android packed
+ // relocations, return the load bias that the system linker (or Chromium
+ // crazy linker) will have used. If the file did not contain Android
+ // packed relocations, returns |start_addr|, indicating that no adjustment
+ // is necessary.
+ // The effective load bias is |start_addr| adjusted downwards by the
+ // min vaddr in the library LOAD segments.
+ uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr);
+
+ // Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr|
+ // field of any that represent ELF shared libraries with Android packed
+ // relocations, so that |start_addr| is the load bias that the system linker
+ // (or Chromium crazy linker) used. This value matches the addresses produced
+ // when the non-relocation-packed library is used for breakpad symbol
+ // generation.
+ void LatePostprocessMappings();
+#endif // __ANDROID__
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper_unittest_helper.cc
new file mode 100644
index 0000000000..3ad48e5015
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper_unittest_helper.cc
@@ -0,0 +1,95 @@
+// 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.
+//
+// Helper program for the linux_dumper class, which creates a bunch of
+// threads. The first word of each thread's stack is set to the thread
+// id.
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "common/scoped_ptr.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+#if defined(__ARM_EABI__)
+#define TID_PTR_REGISTER "r3"
+#elif defined(__aarch64__)
+#define TID_PTR_REGISTER "x3"
+#elif defined(__i386)
+#define TID_PTR_REGISTER "ecx"
+#elif defined(__x86_64)
+#define TID_PTR_REGISTER "rcx"
+#elif defined(__mips__)
+#define TID_PTR_REGISTER "$1"
+#else
+#error This test has not been ported to this platform.
+#endif
+
+void *thread_function(void *data) {
+ int pipefd = *static_cast<int *>(data);
+ volatile pid_t* thread_id = new pid_t;
+ *thread_id = syscall(__NR_gettid);
+ // Signal parent that a thread has started.
+ uint8_t byte = 1;
+ if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
+ perror("ERROR: parent notification failed");
+ return NULL;
+ }
+ register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = thread_id;
+ while (true)
+ asm volatile ("" : : "r" (thread_id_ptr));
+ return NULL;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
+ return 1;
+ }
+ int pipefd = atoi(argv[1]);
+ int num_threads = atoi(argv[2]);
+ if (num_threads < 1) {
+ fprintf(stderr, "ERROR: number of threads is 0");
+ return 1;
+ }
+ google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
+ pthread_attr_t thread_attributes;
+ pthread_attr_init(&thread_attributes);
+ pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
+ for (int i = 1; i < num_threads; i++) {
+ pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
+ }
+ thread_function(&pipefd);
+ return 0;
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc
new file mode 100644
index 0000000000..6ed70eeb61
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc
@@ -0,0 +1,403 @@
+// 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.
+
+// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
+// See linux_ptrace_dumper.h for detals.
+// This class was originally splitted from google_breakpad::LinuxDumper.
+
+// This code deals with the mechanics of getting information about a crashed
+// process. Since this code may run in a compromised address space, the same
+// rules apply as detailed at the top of minidump_writer.h: no libc calls and
+// use the alternative allocator.
+
+#include "linux/minidump_writer/linux_ptrace_dumper.h"
+
+#include <asm/ptrace.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#if defined(__i386)
+#include <cpuid.h>
+#endif
+
+#include "linux/minidump_writer/directory_reader.h"
+#include "linux/minidump_writer/line_reader.h"
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+// Suspends a thread by attaching to it.
+static bool SuspendThread(pid_t pid) {
+ // This may fail if the thread has just died or debugged.
+ errno = 0;
+ if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
+ errno != 0) {
+ return false;
+ }
+ while (sys_waitpid(pid, NULL, __WALL) < 0) {
+ if (errno != EINTR) {
+ sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
+ return false;
+ }
+ }
+#if defined(__i386) || defined(__x86_64)
+ // On x86, the stack pointer is NULL or -1, when executing trusted code in
+ // the seccomp sandbox. Not only does this cause difficulties down the line
+ // when trying to dump the thread's stack, it also results in the minidumps
+ // containing information about the trusted threads. This information is
+ // generally completely meaningless and just pollutes the minidumps.
+ // We thus test the stack pointer and exclude any threads that are part of
+ // the seccomp sandbox's trusted code.
+ user_regs_struct regs;
+ if (sys_ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1 ||
+#if defined(__i386)
+ !regs.esp
+#elif defined(__x86_64)
+ !regs.rsp
+#endif
+ ) {
+ sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
+ return false;
+ }
+#endif
+ return true;
+}
+
+// Resumes a thread by detaching from it.
+static bool ResumeThread(pid_t pid) {
+ return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
+}
+
+namespace google_breakpad {
+
+LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
+ : LinuxDumper(pid),
+ threads_suspended_(false) {
+}
+
+bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
+ const char* node) const {
+ if (!path || !node || pid <= 0)
+ return false;
+
+ size_t node_len = my_strlen(node);
+ if (node_len == 0)
+ return false;
+
+ const unsigned pid_len = my_uint_len(pid);
+ const size_t total_length = 6 + pid_len + 1 + node_len;
+ if (total_length >= NAME_MAX)
+ return false;
+
+ my_memcpy(path, "/proc/", 6);
+ my_uitos(path + 6, pid, pid_len);
+ path[6 + pid_len] = '/';
+ my_memcpy(path + 6 + pid_len + 1, node, node_len);
+ path[total_length] = '\0';
+ return true;
+}
+
+bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
+ const void* src, size_t length) {
+ unsigned long tmp = 55;
+ size_t done = 0;
+ static const size_t word_size = sizeof(tmp);
+ uint8_t* const local = (uint8_t*) dest;
+ uint8_t* const remote = (uint8_t*) src;
+
+ while (done < length) {
+ const size_t l = (length - done > word_size) ? word_size : (length - done);
+ if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
+ tmp = 0;
+ }
+ my_memcpy(local + done, &tmp, l);
+ done += l;
+ }
+ return true;
+}
+
+bool LinuxPtraceDumper::ReadRegisterSet(ThreadInfo* info, pid_t tid)
+{
+#ifdef PTRACE_GETREGSET
+ struct iovec io;
+ info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
+ if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
+ return false;
+ }
+
+ info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
+ if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool LinuxPtraceDumper::ReadRegisters(ThreadInfo* info, pid_t tid) {
+#ifdef PTRACE_GETREGS
+ void* gp_addr;
+ info->GetGeneralPurposeRegisters(&gp_addr, NULL);
+ if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
+ return false;
+ }
+
+#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
+ // When running an arm build on an arm64 device, attempting to get the
+ // floating point registers fails. On Android, the floating point registers
+ // aren't written to the cpu context anyway, so just don't get them here.
+ // See http://crbug.com/508324
+ void* fp_addr;
+ info->GetFloatingPointRegisters(&fp_addr, NULL);
+ if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
+ return false;
+ }
+#endif // !(defined(__ANDROID__) && defined(__ARM_EABI__))
+ return true;
+#else // PTRACE_GETREGS
+ return false;
+#endif
+}
+
+// Read thread info from /proc/$pid/status.
+// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
+// these members are set to -1. Returns true iff all three members are
+// available.
+bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
+ if (index >= threads_.size())
+ return false;
+
+ pid_t tid = threads_[index];
+
+ assert(info != NULL);
+ char status_path[NAME_MAX];
+ if (!BuildProcPath(status_path, tid, "status"))
+ return false;
+
+ const int fd = sys_open(status_path, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+
+ LineReader* const line_reader = new(allocator_) LineReader(fd);
+ const char* line;
+ unsigned line_len;
+
+ info->ppid = info->tgid = -1;
+
+ while (line_reader->GetNextLine(&line, &line_len)) {
+ if (my_strncmp("Tgid:\t", line, 6) == 0) {
+ my_strtoui(&info->tgid, line + 6);
+ } else if (my_strncmp("PPid:\t", line, 6) == 0) {
+ my_strtoui(&info->ppid, line + 6);
+ }
+
+ line_reader->PopLine(line_len);
+ }
+ sys_close(fd);
+
+ if (info->ppid == -1 || info->tgid == -1)
+ return false;
+
+ if (!ReadRegisterSet(info, tid)) {
+ if (!ReadRegisters(info, tid)) {
+ return false;
+ }
+ }
+
+#if defined(__i386)
+#if !defined(bit_FXSAVE) // e.g. Clang
+#define bit_FXSAVE bit_FXSR
+#endif
+ // Detect if the CPU supports the FXSAVE/FXRSTOR instructions
+ int eax, ebx, ecx, edx;
+ __cpuid(1, eax, ebx, ecx, edx);
+ if (edx & bit_FXSAVE) {
+ if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
+ return false;
+ }
+ } else {
+ memset(&info->fpxregs, 0, sizeof(info->fpxregs));
+ }
+#endif // defined(__i386)
+
+#if defined(__i386) || defined(__x86_64)
+ for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
+ if (sys_ptrace(
+ PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*> (offsetof(struct user,
+ u_debugreg[0]) + i *
+ sizeof(debugreg_t)),
+ &info->dregs[i]) == -1) {
+ return false;
+ }
+ }
+#endif
+
+#if defined(__mips__)
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(PC), &info->mcontext.pc);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3);
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
+#endif
+
+ const uint8_t* stack_pointer;
+#if defined(__i386)
+ my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
+#elif defined(__x86_64)
+ my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
+#elif defined(__ARM_EABI__)
+ my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
+#elif defined(__aarch64__)
+ my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
+#elif defined(__mips__)
+ stack_pointer =
+ reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
+#else
+#error "This code hasn't been ported to your platform yet."
+#endif
+ info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
+
+ return true;
+}
+
+bool LinuxPtraceDumper::GetThreadNameByIndex(size_t index, char* name,
+ size_t size) {
+ if (index >= threads_.size())
+ return false;
+
+ pid_t tid = threads_[index];
+
+ assert(name != NULL);
+ char path[NAME_MAX];
+
+ // Read the thread name (aka comm entry in /proc)
+ if (!BuildProcPath(path, tid, "comm"))
+ return false;
+
+ const int fd = sys_open(path, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+
+ const int len = sys_read(fd, name, size);
+ if (len > 0)
+ name[len - 1] = '\0'; // Get rid of the newline
+
+ sys_close(fd);
+
+ return len > 0;
+}
+
+bool LinuxPtraceDumper::IsPostMortem() const {
+ return false;
+}
+
+bool LinuxPtraceDumper::ThreadsSuspend() {
+ if (threads_suspended_)
+ return true;
+ for (size_t i = 0; i < threads_.size(); ++i) {
+ if (!SuspendThread(threads_[i])) {
+ // If the thread either disappeared before we could attach to it, or if
+ // it was part of the seccomp sandbox's trusted code, it is OK to
+ // silently drop it from the minidump.
+ if (i < threads_.size() - 1) {
+ my_memmove(&threads_[i], &threads_[i + 1],
+ (threads_.size() - i - 1) * sizeof(threads_[i]));
+ }
+ threads_.resize(threads_.size() - 1);
+ --i;
+ }
+ }
+ threads_suspended_ = true;
+ return !threads_.empty();
+}
+
+bool LinuxPtraceDumper::ThreadsResume() {
+ if (!threads_suspended_)
+ return false;
+ bool good = true;
+ for (size_t i = 0; i < threads_.size(); ++i)
+ good &= ResumeThread(threads_[i]);
+ threads_suspended_ = false;
+ return good;
+}
+
+// Parse /proc/$pid/task to list all the threads of the process identified by
+// pid.
+bool LinuxPtraceDumper::EnumerateThreads() {
+ char task_path[NAME_MAX];
+ if (!BuildProcPath(task_path, pid_, "task"))
+ return false;
+
+ const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
+ if (fd < 0)
+ return false;
+ DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
+
+ // The directory may contain duplicate entries which we filter by assuming
+ // that they are consecutive.
+ int last_tid = -1;
+ const char* dent_name;
+ while (dir_reader->GetNextEntry(&dent_name)) {
+ if (my_strcmp(dent_name, ".") &&
+ my_strcmp(dent_name, "..")) {
+ int tid = 0;
+ if (my_strtoui(&tid, dent_name) &&
+ last_tid != tid) {
+ last_tid = tid;
+ threads_.push_back(tid);
+ }
+ }
+ dir_reader->PopEntry();
+ }
+
+ sys_close(fd);
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.h
new file mode 100644
index 0000000000..576098c349
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.h
@@ -0,0 +1,106 @@
+// 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.
+
+// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
+// class, which is derived from google_breakpad::LinuxDumper to extract
+// information from a crashed process via ptrace.
+// This class was originally splitted from google_breakpad::LinuxDumper.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
+
+#include "linux/minidump_writer/linux_dumper.h"
+
+namespace google_breakpad {
+
+class LinuxPtraceDumper : public LinuxDumper {
+ public:
+ // Constructs a dumper for extracting information of a given process
+ // with a process ID of |pid|.
+ explicit LinuxPtraceDumper(pid_t pid);
+
+ // Implements LinuxDumper::BuildProcPath().
+ // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
+ // |path| is a character array of at least NAME_MAX bytes to return the
+ // result. |node| is the final node without any slashes. Returns true on
+ // success.
+ virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
+
+ // Implements LinuxDumper::CopyFromProcess().
+ // Copies content of |length| bytes from a given process |child|,
+ // starting from |src|, into |dest|. This method uses ptrace to extract
+ // the content from the target process. Always returns true.
+ virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
+ size_t length);
+
+ // Implements LinuxDumper::GetThreadInfoByIndex().
+ // Reads information about the |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
+
+ // Implements LinuxDumper::GetThreadNameByIndex().
+ // Reads the name of the |index|-th thread of |threads_|.
+ // Returns true on success. One must have called |ThreadsSuspend| first.
+ virtual bool GetThreadNameByIndex(size_t index, char* name, size_t size);
+
+ // Implements LinuxDumper::IsPostMortem().
+ // Always returns false to indicate this dumper performs a dump of
+ // a crashed process via ptrace.
+ virtual bool IsPostMortem() const;
+
+ // Implements LinuxDumper::ThreadsSuspend().
+ // Suspends all threads in the given process. Returns true on success.
+ virtual bool ThreadsSuspend();
+
+ // Implements LinuxDumper::ThreadsResume().
+ // Resumes all threads in the given process. Returns true on success.
+ virtual bool ThreadsResume();
+
+ protected:
+ // Implements LinuxDumper::EnumerateThreads().
+ // Enumerates all threads of the given process into |threads_|.
+ virtual bool EnumerateThreads();
+
+ private:
+ // Set to true if all threads of the crashed process are suspended.
+ bool threads_suspended_;
+
+ // Read the tracee's registers on kernel with PTRACE_GETREGSET support.
+ // Returns false if PTRACE_GETREGSET is not defined.
+ // Returns true on success.
+ bool ReadRegisterSet(ThreadInfo* info, pid_t tid);
+
+ // Read the tracee's registers on kernel with PTRACE_GETREGS support.
+ // Returns true on success.
+ bool ReadRegisters(ThreadInfo* info, pid_t tid);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
new file mode 100644
index 0000000000..79d26a1e31
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
@@ -0,0 +1,580 @@
+// Copyright (c) 2009, 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.
+
+// linux_ptrace_dumper_unittest.cc:
+// Unit tests for google_breakpad::LinuxPtraceDumper.
+//
+// This file was renamed from linux_dumper_unittest.cc and modified due
+// to LinuxDumper being splitted into two classes.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/minidump_writer/linux_ptrace_dumper.h"
+#include "linux/minidump_writer/minidump_writer_unittest_utils.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/file_id.h"
+#include "common/linux/ignore_ret.h"
+#include "common/linux/safe_readlink.h"
+#include "common/memory_allocator.h"
+#include "common/using_std_string.h"
+
+#ifndef PR_SET_PTRACER
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+using namespace google_breakpad;
+
+namespace {
+
+pid_t SetupChildProcess(int number_of_threads) {
+ char kNumberOfThreadsArgument[2];
+ sprintf(kNumberOfThreadsArgument, "%d", number_of_threads);
+
+ int fds[2];
+ EXPECT_NE(-1, pipe(fds));
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ // In child process.
+ close(fds[0]);
+
+ string helper_path(GetHelperBinary());
+ if (helper_path.empty()) {
+ fprintf(stderr, "Couldn't find helper binary\n");
+ _exit(1);
+ }
+
+ // Pass the pipe fd and the number of threads as arguments.
+ char pipe_fd_string[8];
+ sprintf(pipe_fd_string, "%d", fds[1]);
+ execl(helper_path.c_str(),
+ "linux_dumper_unittest_helper",
+ pipe_fd_string,
+ kNumberOfThreadsArgument,
+ NULL);
+ // Kill if we get here.
+ printf("Errno from exec: %d", errno);
+ std::string err_str = "Exec of " + helper_path + " failed";
+ perror(err_str.c_str());
+ _exit(1);
+ }
+ close(fds[1]);
+
+ // Wait for all child threads to indicate that they have started
+ for (int threads = 0; threads < number_of_threads; threads++) {
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fds[0];
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
+ EXPECT_EQ(1, r);
+ EXPECT_TRUE(pfd.revents & POLLIN);
+ uint8_t junk;
+ EXPECT_EQ(read(fds[0], &junk, sizeof(junk)),
+ static_cast<ssize_t>(sizeof(junk)));
+ }
+ close(fds[0]);
+
+ // There is a race here because we may stop a child thread before
+ // it is actually running the busy loop. Empirically this sleep
+ // is sufficient to avoid the race.
+ usleep(100000);
+ return child_pid;
+}
+
+typedef wasteful_vector<uint8_t> id_vector;
+typedef testing::Test LinuxPtraceDumperTest;
+
+/* Fixture for running tests in a child process. */
+class LinuxPtraceDumperChildTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ child_pid_ = fork();
+#ifndef __ANDROID__
+ prctl(PR_SET_PTRACER, child_pid_);
+#endif
+ }
+
+ /* Gtest is calling TestBody from this class, which sets up a child
+ * process in which the RealTestBody virtual member is called.
+ * As such, TestBody is not supposed to be overridden in derived classes.
+ */
+ virtual void TestBody() /* final */ {
+ if (child_pid_ == 0) {
+ // child process
+ RealTestBody();
+ _exit(HasFatalFailure() ? kFatalFailure :
+ (HasNonfatalFailure() ? kNonFatalFailure : 0));
+ }
+
+ ASSERT_TRUE(child_pid_ > 0);
+ int status;
+ waitpid(child_pid_, &status, 0);
+ if (WEXITSTATUS(status) == kFatalFailure) {
+ GTEST_FATAL_FAILURE_("Test failed in child process");
+ } else if (WEXITSTATUS(status) == kNonFatalFailure) {
+ GTEST_NONFATAL_FAILURE_("Test failed in child process");
+ }
+ }
+
+ /* Gtest defines TestBody functions through its macros, but classes
+ * derived from this one need to define RealTestBody instead.
+ * This is achieved by defining a TestBody macro further below.
+ */
+ virtual void RealTestBody() = 0;
+
+ id_vector make_vector() {
+ return id_vector(&allocator, kDefaultBuildIdSize);
+ }
+
+ private:
+ static const int kFatalFailure = 1;
+ static const int kNonFatalFailure = 2;
+
+ pid_t child_pid_;
+ PageAllocator allocator;
+};
+
+} // namespace
+
+/* Replace TestBody declarations within TEST*() with RealTestBody
+ * declarations */
+#define TestBody RealTestBody
+
+TEST_F(LinuxPtraceDumperChildTest, Setup) {
+ LinuxPtraceDumper dumper(getppid());
+}
+
+TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+
+ ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
+ ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
+ ASSERT_FALSE(dumper.FindMapping(NULL));
+}
+
+TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+
+ ASSERT_GE(dumper.threads().size(), (size_t)1);
+ bool found = false;
+ for (size_t i = 0; i < dumper.threads().size(); ++i) {
+ if (dumper.threads()[i] == getppid()) {
+ ASSERT_FALSE(found);
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+}
+
+// Helper stack class to close a file descriptor and unmap
+// a mmap'ed mapping.
+class StackHelper {
+ public:
+ StackHelper()
+ : fd_(-1), mapping_(NULL), size_(0) {}
+ ~StackHelper() {
+ if (size_)
+ munmap(mapping_, size_);
+ if (fd_ >= 0)
+ close(fd_);
+ }
+ void Init(int fd, char* mapping, size_t size) {
+ fd_ = fd;
+ mapping_ = mapping;
+ size_ = size;
+ }
+
+ char* mapping() const { return mapping_; }
+ size_t size() const { return size_; }
+
+ private:
+ int fd_;
+ char* mapping_;
+ size_t size_;
+};
+
+class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
+ protected:
+ virtual void SetUp();
+
+ string helper_path_;
+ size_t page_size_;
+ StackHelper helper_;
+};
+
+void LinuxPtraceDumperMappingsTest::SetUp() {
+ helper_path_ = GetHelperBinary();
+ if (helper_path_.empty()) {
+ FAIL() << "Couldn't find helper binary";
+ _exit(1);
+ }
+
+ // mmap two segments out of the helper binary, one
+ // enclosed in the other, but with different protections.
+ page_size_ = sysconf(_SC_PAGESIZE);
+ const size_t kMappingSize = 3 * page_size_;
+ int fd = open(helper_path_.c_str(), O_RDONLY);
+ ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
+ << ", Error: " << strerror(errno);
+ char* mapping =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMappingSize,
+ PROT_READ,
+ MAP_SHARED,
+ fd,
+ 0));
+ ASSERT_TRUE(mapping);
+
+ // Ensure that things get cleaned up.
+ helper_.Init(fd, mapping, kMappingSize);
+
+ // Carve a page out of the first mapping with different permissions.
+ char* inside_mapping = reinterpret_cast<char*>(
+ mmap(mapping + 2 * page_size_,
+ page_size_,
+ PROT_NONE,
+ MAP_SHARED | MAP_FIXED,
+ fd,
+ // Map a different offset just to
+ // better test real-world conditions.
+ page_size_));
+ ASSERT_TRUE(inside_mapping);
+
+ LinuxPtraceDumperChildTest::SetUp();
+}
+
+TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
+ // Now check that LinuxPtraceDumper interpreted the mappings properly.
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+ int mapping_count = 0;
+ for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
+ const MappingInfo& mapping = *dumper.mappings()[i];
+ if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
+ // This mapping should encompass the entire original mapped
+ // range.
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
+ mapping.start_addr);
+ EXPECT_EQ(this->helper_.size(), mapping.size);
+ EXPECT_EQ(0U, mapping.offset);
+ mapping_count++;
+ }
+ }
+ EXPECT_EQ(1, mapping_count);
+}
+
+TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
+ const pid_t pid = getppid();
+ LinuxPtraceDumper dumper(pid);
+
+ char maps_path[NAME_MAX] = "";
+ char maps_path_expected[NAME_MAX];
+ snprintf(maps_path_expected, sizeof(maps_path_expected),
+ "/proc/%d/maps", pid);
+ EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
+ EXPECT_STREQ(maps_path_expected, maps_path);
+
+ EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
+
+ char long_node[NAME_MAX];
+ size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
+ memset(long_node, 'a', long_node_len);
+ long_node[long_node_len] = '\0';
+ EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
+}
+
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+// Ensure that the linux-gate VDSO is included in the mapping list.
+TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+
+ void* linux_gate_loc =
+ reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
+ ASSERT_TRUE(linux_gate_loc);
+ bool found_linux_gate = false;
+
+ const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+ const MappingInfo* mapping;
+ for (unsigned i = 0; i < mappings.size(); ++i) {
+ mapping = mappings[i];
+ if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
+ found_linux_gate = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found_linux_gate);
+ EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
+ EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
+}
+
+// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
+TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+
+ bool found_linux_gate = false;
+ const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+ unsigned index = 0;
+ for (unsigned i = 0; i < mappings.size(); ++i) {
+ if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
+ found_linux_gate = true;
+ index = i;
+ break;
+ }
+ }
+ ASSERT_TRUE(found_linux_gate);
+
+ // Need to suspend the child so ptrace actually works.
+ ASSERT_TRUE(dumper.ThreadsSuspend());
+ id_vector identifier(make_vector());
+ ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
+ true,
+ index,
+ identifier));
+
+ id_vector empty_identifier(make_vector());
+ empty_identifier.resize(kDefaultBuildIdSize, 0);
+ EXPECT_NE(empty_identifier, identifier);
+ EXPECT_TRUE(dumper.ThreadsResume());
+}
+#endif
+
+TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
+ // Calculate the File ID of our binary using both
+ // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
+ // and ensure that we get the same result from both.
+ char exe_name[PATH_MAX];
+ ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
+
+ LinuxPtraceDumper dumper(getppid());
+ ASSERT_TRUE(dumper.Init());
+ const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+ bool found_exe = false;
+ unsigned i;
+ for (i = 0; i < mappings.size(); ++i) {
+ const MappingInfo* mapping = mappings[i];
+ if (!strcmp(mapping->name, exe_name)) {
+ found_exe = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found_exe);
+
+ id_vector identifier1(make_vector());
+ id_vector identifier2(make_vector());
+ EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
+ identifier1));
+ FileID fileid(exe_name);
+ EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
+
+ string identifier_string1 =
+ FileID::ConvertIdentifierToUUIDString(identifier1);
+ string identifier_string2 =
+ FileID::ConvertIdentifierToUUIDString(identifier2);
+ EXPECT_EQ(identifier_string1, identifier_string2);
+}
+
+/* Get back to normal behavior of TEST*() macros wrt TestBody. */
+#undef TestBody
+
+TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
+ static const size_t kNumberOfThreadsInHelperProgram = 5;
+
+ pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
+ ASSERT_NE(child_pid, -1);
+
+ // Children are ready now.
+ LinuxPtraceDumper dumper(child_pid);
+ ASSERT_TRUE(dumper.Init());
+#if defined(THREAD_SANITIZER)
+ EXPECT_GE(dumper.threads().size(), (size_t)kNumberOfThreadsInHelperProgram);
+#else
+ EXPECT_EQ(dumper.threads().size(), (size_t)kNumberOfThreadsInHelperProgram);
+#endif
+ EXPECT_TRUE(dumper.ThreadsSuspend());
+
+ ThreadInfo one_thread;
+ size_t matching_threads = 0;
+ for (size_t i = 0; i < dumper.threads().size(); ++i) {
+ EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
+ const void* stack;
+ size_t stack_len;
+ EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
+ one_thread.stack_pointer));
+ // In the helper program, we stored a pointer to the thread id in a
+ // specific register. Check that we can recover its value.
+#if defined(__ARM_EABI__)
+ pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
+#elif defined(__aarch64__)
+ pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
+#elif defined(__i386)
+ pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
+#elif defined(__x86_64)
+ pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
+#elif defined(__mips__)
+ pid_t* process_tid_location =
+ reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
+#else
+#error This test has not been ported to this platform.
+#endif
+ pid_t one_thread_id;
+ dumper.CopyFromProcess(&one_thread_id,
+ dumper.threads()[i],
+ process_tid_location,
+ 4);
+ matching_threads += (dumper.threads()[i] == one_thread_id) ? 1 : 0;
+ }
+ EXPECT_EQ(matching_threads, kNumberOfThreadsInHelperProgram);
+ EXPECT_TRUE(dumper.ThreadsResume());
+ kill(child_pid, SIGKILL);
+
+ // Reap child
+ int status;
+ ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) {
+ static const size_t kNumberOfThreadsInHelperProgram = 1;
+
+ pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
+ ASSERT_NE(child_pid, -1);
+
+ LinuxPtraceDumper dumper(child_pid);
+ ASSERT_TRUE(dumper.Init());
+ EXPECT_TRUE(dumper.ThreadsSuspend());
+
+ ThreadInfo thread_info;
+ EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
+
+ const uintptr_t defaced =
+#if defined(__LP64__)
+ 0x0defaced0defaced;
+#else
+ 0x0defaced;
+#endif
+
+ uintptr_t simulated_stack[2];
+
+ // Pointers into the stack shouldn't be sanitized.
+ memset(simulated_stack, 0xff, sizeof(simulated_stack));
+ simulated_stack[1] = thread_info.stack_pointer;
+ dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
+ sizeof(simulated_stack), thread_info.stack_pointer,
+ sizeof(uintptr_t));
+ ASSERT_NE(simulated_stack[1], defaced);
+
+ // Memory prior to the stack pointer should be cleared.
+ ASSERT_EQ(simulated_stack[0], 0u);
+
+ // Small integers should not be sanitized.
+ for (int i = -4096; i <= 4096; ++i) {
+ memset(simulated_stack, 0, sizeof(simulated_stack));
+ simulated_stack[0] = static_cast<uintptr_t>(i);
+ dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
+ sizeof(simulated_stack), thread_info.stack_pointer,
+ 0u);
+ ASSERT_NE(simulated_stack[0], defaced);
+ }
+
+ // The instruction pointer definitely should point into an executable mapping.
+ const MappingInfo* mapping_info = dumper.FindMappingNoBias(
+ reinterpret_cast<uintptr_t>(thread_info.GetInstructionPointer()));
+ ASSERT_NE(mapping_info, nullptr);
+ ASSERT_TRUE(mapping_info->exec);
+
+ // Pointers to code shouldn't be sanitized.
+ memset(simulated_stack, 0, sizeof(simulated_stack));
+ simulated_stack[1] = thread_info.GetInstructionPointer();
+ dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
+ sizeof(simulated_stack), thread_info.stack_pointer,
+ 0u);
+ ASSERT_NE(simulated_stack[0], defaced);
+
+ // String fragments should be sanitized.
+ memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack));
+ dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
+ sizeof(simulated_stack), thread_info.stack_pointer,
+ 0u);
+ ASSERT_EQ(simulated_stack[0], defaced);
+ ASSERT_EQ(simulated_stack[1], defaced);
+
+ // Heap pointers should be sanititzed.
+#if defined(__ARM_EABI__)
+ uintptr_t heap_addr = thread_info.regs.uregs[3];
+#elif defined(__aarch64__)
+ uintptr_t heap_addr = thread_info.regs.regs[3];
+#elif defined(__i386)
+ uintptr_t heap_addr = thread_info.regs.ecx;
+#elif defined(__x86_64)
+ uintptr_t heap_addr = thread_info.regs.rcx;
+#elif defined(__mips__)
+ uintptr_t heap_addr = thread_info.mcontext.gregs[1];
+#else
+#error This test has not been ported to this platform.
+#endif
+ memset(simulated_stack, 0, sizeof(simulated_stack));
+ simulated_stack[0] = heap_addr;
+ dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
+ sizeof(simulated_stack), thread_info.stack_pointer,
+ 0u);
+ ASSERT_EQ(simulated_stack[0], defaced);
+
+ EXPECT_TRUE(dumper.ThreadsResume());
+ kill(child_pid, SIGKILL);
+
+ // Reap child.
+ int status;
+ ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(SIGKILL, WTERMSIG(status));
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc
new file mode 100644
index 0000000000..03066e9110
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc
@@ -0,0 +1,1562 @@
+// 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.
+
+// This code writes out minidump files:
+// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
+//
+// Minidumps are a Microsoft format which Breakpad uses for recording crash
+// dumps. This code has to run in a compromised environment (the address space
+// may have received SIGSEGV), thus the following rules apply:
+// * You may not enter the dynamic linker. This means that we cannot call
+// any symbols in a shared library (inc libc). Because of this we replace
+// libc functions in linux_libc_support.h.
+// * You may not call syscalls via the libc wrappers. This rule is a subset
+// of the first rule but it bears repeating. We have direct wrappers
+// around the system calls in linux_syscall_support.h.
+// * You may not malloc. There's an alternative allocator in memory.h and
+// a canonical instance in the LinuxDumper object. We use the placement
+// new form to allocate objects and we don't delete them.
+
+#include "linux/handler/minidump_descriptor.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "minidump_file_writer-inl.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stdio.h>
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+#endif
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <sys/user.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "linux/dump_writer_common/thread_info.h"
+#include "linux/dump_writer_common/ucontext_reader.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/minidump_writer/cpu_set.h"
+#include "linux/minidump_writer/line_reader.h"
+#include "linux/minidump_writer/linux_dumper.h"
+#include "linux/minidump_writer/linux_ptrace_dumper.h"
+#include "linux/minidump_writer/proc_cpuinfo_reader.h"
+#include "minidump_file_writer.h"
+#include "common/linux/file_id.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/minidump_type_helper.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace {
+
+using google_breakpad::AppMemoryList;
+using google_breakpad::auto_wasteful_vector;
+using google_breakpad::ExceptionHandler;
+using google_breakpad::CpuSet;
+using google_breakpad::kDefaultBuildIdSize;
+using google_breakpad::LineReader;
+using google_breakpad::LinuxDumper;
+using google_breakpad::LinuxPtraceDumper;
+using google_breakpad::MDTypeHelper;
+using google_breakpad::MappingEntry;
+using google_breakpad::MappingInfo;
+using google_breakpad::MappingList;
+using google_breakpad::MinidumpFileWriter;
+using google_breakpad::PageAllocator;
+using google_breakpad::ProcCpuInfoReader;
+using google_breakpad::RawContextCPU;
+using google_breakpad::ThreadInfo;
+using google_breakpad::TypedMDRVA;
+using google_breakpad::UContextReader;
+using google_breakpad::UntypedMDRVA;
+using google_breakpad::wasteful_vector;
+
+typedef MDTypeHelper<sizeof(void*)>::MDRawDebug MDRawDebug;
+typedef MDTypeHelper<sizeof(void*)>::MDRawLinkMap MDRawLinkMap;
+
+class MinidumpWriter {
+ public:
+ // The following kLimit* constants are for when minidump_size_limit_ is set
+ // and the minidump size might exceed it.
+ //
+ // Estimate for how big each thread's stack will be (in bytes).
+ static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
+ // Number of threads whose stack size we don't want to limit. These base
+ // threads will simply be the first N threads returned by the dumper (although
+ // the crashing thread will never be limited). Threads beyond this count are
+ // the extra threads.
+ static const unsigned kLimitBaseThreadCount = 20;
+ // Maximum stack size to dump for any extra thread (in bytes).
+ static const unsigned kLimitMaxExtraThreadStackLen = 2 * 1024;
+ // Make sure this number of additional bytes can fit in the minidump
+ // (exclude the stack data).
+ static const unsigned kLimitMinidumpFudgeFactor = 64 * 1024;
+
+ MinidumpWriter(const char* minidump_path,
+ int minidump_fd,
+ const ExceptionHandler::CrashContext* context,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks,
+ LinuxDumper* dumper)
+ : fd_(minidump_fd),
+ path_(minidump_path),
+ ucontext_(context ? &context->context : NULL),
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ float_state_(context ? &context->float_state : NULL),
+#endif
+ dumper_(dumper),
+ minidump_size_limit_(-1),
+ memory_blocks_(dumper_->allocator()),
+ mapping_list_(mappings),
+ app_memory_list_(appmem),
+ skip_stacks_if_mapping_unreferenced_(
+ skip_stacks_if_mapping_unreferenced),
+ principal_mapping_address_(principal_mapping_address),
+ principal_mapping_(nullptr),
+ sanitize_stacks_(sanitize_stacks) {
+ // Assert there should be either a valid fd or a valid path, not both.
+ assert(fd_ != -1 || minidump_path);
+ assert(fd_ == -1 || !minidump_path);
+ }
+
+ bool Init() {
+ if (!dumper_->Init())
+ return false;
+
+ if (!dumper_->ThreadsSuspend() || !dumper_->LateInit())
+ return false;
+
+ if (skip_stacks_if_mapping_unreferenced_) {
+ principal_mapping_ =
+ dumper_->FindMappingNoBias(principal_mapping_address_);
+ if (!CrashingThreadReferencesPrincipalMapping())
+ return false;
+ }
+
+ if (fd_ != -1)
+ minidump_writer_.SetFile(fd_);
+ else if (!minidump_writer_.Open(path_))
+ return false;
+
+ return true;
+ }
+
+ ~MinidumpWriter() {
+ // Don't close the file descriptor when it's been provided explicitly.
+ // Callers might still need to use it.
+ if (fd_ == -1)
+ minidump_writer_.Close();
+ dumper_->ThreadsResume();
+ }
+
+ bool CrashingThreadReferencesPrincipalMapping() {
+ if (!ucontext_ || !principal_mapping_)
+ return false;
+
+ const uintptr_t low_addr =
+ principal_mapping_->system_mapping_info.start_addr;
+ const uintptr_t high_addr =
+ principal_mapping_->system_mapping_info.end_addr;
+
+ const uintptr_t stack_pointer = UContextReader::GetStackPointer(ucontext_);
+ const uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
+
+ if (pc >= low_addr && pc < high_addr)
+ return true;
+
+ uint8_t* stack_copy;
+ const void* stack;
+ size_t stack_len;
+
+ if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer))
+ return false;
+
+ stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
+ dumper_->CopyFromProcess(stack_copy, GetCrashThread(), stack, stack_len);
+
+ uintptr_t stack_pointer_offset =
+ stack_pointer - reinterpret_cast<uintptr_t>(stack);
+
+ return dumper_->StackHasPointerToMapping(
+ stack_copy, stack_len, stack_pointer_offset, *principal_mapping_);
+ }
+
+ bool Dump() {
+ // A minidump file contains a number of tagged streams. This is the number
+ // of stream which we write.
+ unsigned kNumWriters = 14;
+
+ TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
+ {
+ // Ensure the header gets flushed, as that happens in the destructor.
+ // If we crash somewhere below, we should have a mostly-intact dump
+ TypedMDRVA<MDRawHeader> header(&minidump_writer_);
+ if (!header.Allocate())
+ return false;
+
+ if (!dir.AllocateArray(kNumWriters))
+ return false;
+
+ my_memset(header.get(), 0, sizeof(MDRawHeader));
+
+ header.get()->signature = MD_HEADER_SIGNATURE;
+ header.get()->version = MD_HEADER_VERSION;
+ header.get()->time_date_stamp = time(NULL);
+ header.get()->stream_count = kNumWriters;
+ header.get()->stream_directory_rva = dir.position();
+ }
+
+ unsigned dir_index = 0;
+ MDRawDirectory dirent;
+
+ if (!WriteThreadListStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteThreadNamesStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteMappings(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteAppMemory())
+ return false;
+
+ if (!WriteMemoryListStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteExceptionStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteSystemInfoStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_CPU_INFO;
+ if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_PROC_STATUS;
+ if (!WriteProcFile(&dirent.location, GetCrashThread(), "status"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_LSB_RELEASE;
+ if (!WriteFile(&dirent.location, "/etc/lsb-release") &&
+ !WriteFile(&dirent.location, "/etc/os-release")) {
+ NullifyDirectoryEntry(&dirent);
+ }
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_CMD_LINE;
+ if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_ENVIRON;
+ if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_AUXV;
+ if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_MAPS;
+ if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_DSO_DEBUG;
+ if (!WriteDSODebugStream(&dirent))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ // If you add more directory entries, don't forget to update kNumWriters,
+ // above.
+
+ dumper_->ThreadsResume();
+ return true;
+ }
+
+ bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
+ uintptr_t pc, int max_stack_len, uint8_t** stack_copy) {
+ *stack_copy = NULL;
+ const void* stack;
+ size_t stack_len;
+
+ thread->stack.start_of_memory_range = stack_pointer;
+ thread->stack.memory.data_size = 0;
+ thread->stack.memory.rva = minidump_writer_.position();
+
+ if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
+ if (max_stack_len >= 0 &&
+ stack_len > static_cast<unsigned int>(max_stack_len)) {
+ stack_len = max_stack_len;
+ // Skip empty chunks of length max_stack_len.
+ uintptr_t int_stack = reinterpret_cast<uintptr_t>(stack);
+ if (max_stack_len > 0) {
+ while (int_stack + max_stack_len < stack_pointer) {
+ int_stack += max_stack_len;
+ }
+ }
+ stack = reinterpret_cast<const void*>(int_stack);
+ }
+ *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
+ dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack,
+ stack_len);
+
+ uintptr_t stack_pointer_offset =
+ stack_pointer - reinterpret_cast<uintptr_t>(stack);
+ if (skip_stacks_if_mapping_unreferenced_) {
+ if (!principal_mapping_) {
+ return true;
+ }
+ uintptr_t low_addr = principal_mapping_->system_mapping_info.start_addr;
+ uintptr_t high_addr = principal_mapping_->system_mapping_info.end_addr;
+ if ((pc < low_addr || pc > high_addr) &&
+ !dumper_->StackHasPointerToMapping(*stack_copy, stack_len,
+ stack_pointer_offset,
+ *principal_mapping_)) {
+ return true;
+ }
+ }
+
+ if (sanitize_stacks_) {
+ dumper_->SanitizeStackCopy(*stack_copy, stack_len, stack_pointer,
+ stack_pointer_offset);
+ }
+
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(stack_len))
+ return false;
+ memory.Copy(*stack_copy, stack_len);
+ thread->stack.start_of_memory_range = reinterpret_cast<uintptr_t>(stack);
+ thread->stack.memory = memory.location();
+ memory_blocks_.push_back(thread->stack);
+ }
+ return true;
+ }
+
+ // Write information about the threads.
+ bool WriteThreadListStream(MDRawDirectory* dirent) {
+ const unsigned num_threads = dumper_->threads().size();
+
+ TypedMDRVA<uint32_t> list(&minidump_writer_);
+ if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
+ return false;
+
+ dirent->stream_type = MD_THREAD_LIST_STREAM;
+ dirent->location = list.location();
+
+ *list.get() = num_threads;
+
+ // If there's a minidump size limit, check if it might be exceeded. Since
+ // most of the space is filled with stack data, just check against that.
+ // If this expects to exceed the limit, set extra_thread_stack_len such
+ // that any thread beyond the first kLimitBaseThreadCount threads will
+ // have only kLimitMaxExtraThreadStackLen bytes dumped.
+ int extra_thread_stack_len = -1; // default to no maximum
+ if (minidump_size_limit_ >= 0) {
+ const unsigned estimated_total_stack_size = num_threads *
+ kLimitAverageThreadStackLength;
+ const off_t estimated_minidump_size = minidump_writer_.position() +
+ estimated_total_stack_size + kLimitMinidumpFudgeFactor;
+ if (estimated_minidump_size > minidump_size_limit_)
+ extra_thread_stack_len = kLimitMaxExtraThreadStackLen;
+ }
+
+ for (unsigned i = 0; i < num_threads; ++i) {
+ MDRawThread thread;
+ my_memset(&thread, 0, sizeof(thread));
+ thread.thread_id = dumper_->threads()[i];
+
+ // We have a different source of information for the crashing thread. If
+ // we used the actual state of the thread we would find it running in the
+ // signal handler with the alternative stack, which would be deeply
+ // unhelpful.
+ if (static_cast<pid_t>(thread.thread_id) == GetCrashThread() &&
+ ucontext_ &&
+ !dumper_->IsPostMortem()) {
+ uint8_t* stack_copy;
+ const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
+ if (!FillThreadStack(&thread, stack_ptr,
+ UContextReader::GetInstructionPointer(ucontext_),
+ -1, &stack_copy))
+ return false;
+
+ // Copy 256 bytes around crashing instruction pointer to minidump.
+ const size_t kIPMemorySize = 256;
+ uint64_t ip = UContextReader::GetInstructionPointer(ucontext_);
+ // Bound it to the upper and lower bounds of the memory map
+ // it's contained within. If it's not in mapped memory,
+ // don't bother trying to write it.
+ bool ip_is_mapped = false;
+ MDMemoryDescriptor ip_memory_d;
+ for (unsigned j = 0; j < dumper_->mappings().size(); ++j) {
+ const MappingInfo& mapping = *dumper_->mappings()[j];
+ if (ip >= mapping.start_addr &&
+ ip < mapping.start_addr + mapping.size) {
+ ip_is_mapped = true;
+ // Try to get 128 bytes before and after the IP, but
+ // settle for whatever's available.
+ ip_memory_d.start_of_memory_range =
+ std::max(mapping.start_addr,
+ uintptr_t(ip - (kIPMemorySize / 2)));
+ uintptr_t end_of_range =
+ std::min(uintptr_t(ip + (kIPMemorySize / 2)),
+ uintptr_t(mapping.start_addr + mapping.size));
+ ip_memory_d.memory.data_size =
+ end_of_range - ip_memory_d.start_of_memory_range;
+ break;
+ }
+ }
+
+ if (ip_is_mapped) {
+ UntypedMDRVA ip_memory(&minidump_writer_);
+ if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
+ return false;
+ uint8_t* memory_copy =
+ reinterpret_cast<uint8_t*>(Alloc(ip_memory_d.memory.data_size));
+ dumper_->CopyFromProcess(
+ memory_copy,
+ thread.thread_id,
+ reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
+ ip_memory_d.memory.data_size);
+ ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
+ ip_memory_d.memory = ip_memory.location();
+ memory_blocks_.push_back(ip_memory_d);
+ }
+
+ TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+ if (!cpu.Allocate())
+ return false;
+ my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_);
+#else
+ UContextReader::FillCPUContext(cpu.get(), ucontext_);
+#endif
+ thread.thread_context = cpu.location();
+ crashing_thread_context_ = cpu.location();
+ } else {
+ ThreadInfo info;
+ if (!dumper_->GetThreadInfoByIndex(i, &info))
+ return false;
+
+ uint8_t* stack_copy;
+ int max_stack_len = -1; // default to no maximum for this thread
+ if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount)
+ max_stack_len = extra_thread_stack_len;
+ if (!FillThreadStack(&thread, info.stack_pointer,
+ info.GetInstructionPointer(), max_stack_len,
+ &stack_copy))
+ return false;
+
+ TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+ if (!cpu.Allocate())
+ return false;
+ my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+ info.FillCPUContext(cpu.get());
+ thread.thread_context = cpu.location();
+ if (dumper_->threads()[i] == GetCrashThread()) {
+ crashing_thread_context_ = cpu.location();
+ if (!dumper_->IsPostMortem()) {
+ // This is the crashing thread of a live process, but
+ // no context was provided, so set the crash address
+ // while the instruction pointer is already here.
+ dumper_->set_crash_address(info.GetInstructionPointer());
+ }
+ }
+ }
+
+ list.CopyIndexAfterObject(i, &thread, sizeof(thread));
+ }
+
+ return true;
+ }
+
+ bool WriteThreadName(pid_t tid, char* name, MDRawThreadName *thread_name) {
+ MDLocationDescriptor string_location;
+
+ if (!minidump_writer_.WriteString(name, 0, &string_location))
+ return false;
+
+ thread_name->thread_id = tid;
+ thread_name->rva_of_thread_name = string_location.rva;
+ return true;
+ }
+
+ // Write the threads' names.
+ bool WriteThreadNamesStream(MDRawDirectory* thread_names_stream) {
+ TypedMDRVA<MDRawThreadNamesList> list(&minidump_writer_);
+ const unsigned num_threads = dumper_->threads().size();
+
+ if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThreadName))) {
+ return false;
+ }
+
+ thread_names_stream->stream_type = MD_THREAD_NAMES_STREAM;
+ thread_names_stream->location = list.location();
+ list.get()->number_of_thread_names = num_threads;
+
+ MDRawThreadName thread_name;
+ int thread_idx = 0;
+
+ for (unsigned int i = 0; i < num_threads; ++i) {
+ const pid_t tid = dumper_->threads()[i];
+ // This is a constant from the Linux kernel, documented in man 5 proc.
+ // The comm entries in /proc are no longer than this.
+ static const size_t TASK_COMM_LEN = 16;
+ char name[TASK_COMM_LEN];
+ memset(&thread_name, 0, sizeof(MDRawThreadName));
+
+ if (dumper_->GetThreadNameByIndex(i, name, sizeof(name))) {
+ if (WriteThreadName(tid, name, &thread_name)) {
+ list.CopyIndexAfterObject(thread_idx++, &thread_name,
+ sizeof(MDRawThreadName));
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // Write application-provided memory regions.
+ bool WriteAppMemory() {
+ for (AppMemoryList::const_iterator iter = app_memory_list_.begin();
+ iter != app_memory_list_.end();
+ ++iter) {
+ uint8_t* data_copy =
+ reinterpret_cast<uint8_t*>(dumper_->allocator()->Alloc(iter->length));
+ dumper_->CopyFromProcess(data_copy, GetCrashThread(), iter->ptr,
+ iter->length);
+
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(iter->length)) {
+ return false;
+ }
+ memory.Copy(data_copy, iter->length);
+ MDMemoryDescriptor desc;
+ desc.start_of_memory_range = reinterpret_cast<uintptr_t>(iter->ptr);
+ desc.memory = memory.location();
+ memory_blocks_.push_back(desc);
+ }
+
+ return true;
+ }
+
+ static bool ShouldIncludeMapping(const MappingInfo& mapping) {
+ if (mapping.name[0] == 0 || // only want modules with filenames.
+ // Only want to include one mapping per shared lib.
+ // Avoid filtering executable mappings.
+ (mapping.offset != 0 && !mapping.exec) ||
+ mapping.size < 4096) { // too small to get a signature for.
+ return false;
+ }
+
+ return true;
+ }
+
+ // If there is caller-provided information about this mapping
+ // in the mapping_list_ list, return true. Otherwise, return false.
+ bool HaveMappingInfo(const MappingInfo& mapping) {
+ for (MappingList::const_iterator iter = mapping_list_.begin();
+ iter != mapping_list_.end();
+ ++iter) {
+ // Ignore any mappings that are wholly contained within
+ // mappings in the mapping_info_ list.
+ if (mapping.start_addr >= iter->first.start_addr &&
+ (mapping.start_addr + mapping.size) <=
+ (iter->first.start_addr + iter->first.size)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Write information about the mappings in effect. Because we are using the
+ // minidump format, the information about the mappings is pretty limited.
+ // Because of this, we also include the full, unparsed, /proc/$x/maps file in
+ // another stream in the file.
+ bool WriteMappings(MDRawDirectory* dirent) {
+ const unsigned num_mappings = dumper_->mappings().size();
+ unsigned num_output_mappings = mapping_list_.size();
+
+ for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
+ const MappingInfo& mapping = *dumper_->mappings()[i];
+ if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping))
+ num_output_mappings++;
+ }
+
+ TypedMDRVA<uint32_t> list(&minidump_writer_);
+ if (num_output_mappings) {
+ if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
+ return false;
+ } else {
+ // Still create the module list stream, although it will have zero
+ // modules.
+ if (!list.Allocate())
+ return false;
+ }
+
+ dirent->stream_type = MD_MODULE_LIST_STREAM;
+ dirent->location = list.location();
+ *list.get() = num_output_mappings;
+
+ // First write all the mappings from the dumper
+ unsigned int j = 0;
+ for (unsigned i = 0; i < num_mappings; ++i) {
+ const MappingInfo& mapping = *dumper_->mappings()[i];
+ if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping))
+ continue;
+
+ MDRawModule mod;
+ if (!FillRawModule(mapping, true, i, &mod, NULL))
+ return false;
+ list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
+ }
+ // Next write all the mappings provided by the caller
+ for (MappingList::const_iterator iter = mapping_list_.begin();
+ iter != mapping_list_.end();
+ ++iter) {
+ MDRawModule mod;
+ if (!FillRawModule(iter->first, false, 0, &mod, &iter->second)) {
+ return false;
+ }
+ list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
+ }
+
+ return true;
+ }
+
+ // Fill the MDRawModule |mod| with information about the provided
+ // |mapping|. If |identifier| is non-NULL, use it instead of calculating
+ // a file ID from the mapping.
+ bool FillRawModule(const MappingInfo& mapping,
+ bool member,
+ unsigned int mapping_id,
+ MDRawModule* mod,
+ const std::vector<uint8_t>* identifier) {
+ my_memset(mod, 0, MD_MODULE_SIZE);
+
+ mod->base_of_image = mapping.start_addr;
+ mod->size_of_image = mapping.size;
+
+ auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
+ dumper_->allocator());
+
+ if (identifier) {
+ // GUID was provided by caller.
+ identifier_bytes.insert(identifier_bytes.end(),
+ identifier->begin(),
+ identifier->end());
+ } else {
+ // Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|.
+ if (!dumper_->ElfFileIdentifierForMapping(mapping,
+ member,
+ mapping_id,
+ identifier_bytes)) {
+ identifier_bytes.clear();
+ }
+ }
+
+ if (!identifier_bytes.empty()) {
+ UntypedMDRVA cv(&minidump_writer_);
+ if (!cv.Allocate(MDCVInfoELF_minsize + identifier_bytes.size()))
+ return false;
+
+ const uint32_t cv_signature = MD_CVINFOELF_SIGNATURE;
+ cv.Copy(&cv_signature, sizeof(cv_signature));
+ cv.Copy(cv.position() + sizeof(cv_signature), &identifier_bytes[0],
+ identifier_bytes.size());
+
+ mod->cv_record = cv.location();
+ }
+
+ char file_name[NAME_MAX];
+ char file_path[NAME_MAX];
+ dumper_->GetMappingEffectiveNameAndPath(
+ mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
+
+ MDLocationDescriptor ld;
+ if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))
+ return false;
+ mod->module_name_rva = ld.rva;
+ return true;
+ }
+
+ bool WriteMemoryListStream(MDRawDirectory* dirent) {
+ TypedMDRVA<uint32_t> list(&minidump_writer_);
+ if (!memory_blocks_.empty()) {
+ if (!list.AllocateObjectAndArray(memory_blocks_.size(),
+ sizeof(MDMemoryDescriptor)))
+ return false;
+ } else {
+ // Still create the memory list stream, although it will have zero
+ // memory blocks.
+ if (!list.Allocate())
+ return false;
+ }
+
+ dirent->stream_type = MD_MEMORY_LIST_STREAM;
+ dirent->location = list.location();
+
+ *list.get() = memory_blocks_.size();
+
+ for (size_t i = 0; i < memory_blocks_.size(); ++i) {
+ list.CopyIndexAfterObject(i, &memory_blocks_[i],
+ sizeof(MDMemoryDescriptor));
+ }
+ return true;
+ }
+
+ bool WriteExceptionStream(MDRawDirectory* dirent) {
+ TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
+ if (!exc.Allocate())
+ return false;
+
+ MDRawExceptionStream* stream = exc.get();
+ my_memset(stream, 0, sizeof(MDRawExceptionStream));
+
+ dirent->stream_type = MD_EXCEPTION_STREAM;
+ dirent->location = exc.location();
+
+ stream->thread_id = GetCrashThread();
+ stream->exception_record.exception_code = dumper_->crash_signal();
+ stream->exception_record.exception_flags = dumper_->crash_signal_code();
+ stream->exception_record.exception_address = dumper_->crash_address();
+ const std::vector<uint64_t> crash_exception_info =
+ dumper_->crash_exception_info();
+ stream->exception_record.number_parameters = crash_exception_info.size();
+ memcpy(stream->exception_record.exception_information,
+ crash_exception_info.data(),
+ sizeof(uint64_t) * crash_exception_info.size());
+ stream->thread_context = crashing_thread_context_;
+
+ return true;
+ }
+
+ bool WriteSystemInfoStream(MDRawDirectory* dirent) {
+ TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_);
+ if (!si.Allocate())
+ return false;
+ my_memset(si.get(), 0, sizeof(MDRawSystemInfo));
+
+ dirent->stream_type = MD_SYSTEM_INFO_STREAM;
+ dirent->location = si.location();
+
+ WriteCPUInformation(si.get());
+ WriteOSInformation(si.get());
+
+ return true;
+ }
+
+ bool WriteDSODebugStream(MDRawDirectory* dirent) {
+ ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr) *>(dumper_->auxv()[AT_PHDR]);
+ char* base;
+ int phnum = dumper_->auxv()[AT_PHNUM];
+ if (!phnum || !phdr)
+ return false;
+
+ // Assume the program base is at the beginning of the same page as the PHDR
+ base = reinterpret_cast<char *>(reinterpret_cast<uintptr_t>(phdr) & ~0xfff);
+
+ // Search for the program PT_DYNAMIC segment
+ ElfW(Addr) dyn_addr = 0;
+ for (; phnum >= 0; phnum--, phdr++) {
+ ElfW(Phdr) ph;
+ if (!dumper_->CopyFromProcess(&ph, dumper_->pid(), phdr, sizeof(ph)))
+ return false;
+
+ // Adjust base address with the virtual address of the PT_LOAD segment
+ // corresponding to offset 0
+ if (ph.p_type == PT_LOAD && ph.p_offset == 0) {
+ base -= ph.p_vaddr;
+ }
+ if (ph.p_type == PT_DYNAMIC) {
+ dyn_addr = ph.p_vaddr;
+ }
+ }
+ if (!dyn_addr)
+ return false;
+
+ ElfW(Dyn) *dynamic = reinterpret_cast<ElfW(Dyn) *>(dyn_addr + base);
+
+ // The dynamic linker makes information available that helps gdb find all
+ // DSOs loaded into the program. If this information is indeed available,
+ // dump it to a MD_LINUX_DSO_DEBUG stream.
+ struct r_debug* r_debug = NULL;
+ uint32_t dynamic_length = 0;
+
+ for (int i = 0; ; ++i) {
+ ElfW(Dyn) dyn;
+ dynamic_length += sizeof(dyn);
+ if (!dumper_->CopyFromProcess(&dyn, dumper_->pid(), dynamic + i, sizeof(dyn))) {
+ return false;
+ }
+
+#ifdef __mips__
+ const int32_t debug_tag = DT_MIPS_RLD_MAP;
+#else
+ const int32_t debug_tag = DT_DEBUG;
+#endif
+ if (dyn.d_tag == debug_tag) {
+ r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
+ continue;
+ } else if (dyn.d_tag == DT_NULL) {
+ break;
+ }
+ }
+
+ // The "r_map" field of that r_debug struct contains a linked list of all
+ // loaded DSOs.
+ // Our list of DSOs potentially is different from the ones in the crashing
+ // process. So, we have to be careful to never dereference pointers
+ // directly. Instead, we use CopyFromProcess() everywhere.
+ // See <link.h> for a more detailed discussion of the how the dynamic
+ // loader communicates with debuggers.
+
+ // Count the number of loaded DSOs
+ int dso_count = 0;
+ struct r_debug debug_entry;
+ if (!dumper_->CopyFromProcess(&debug_entry, dumper_->pid(), r_debug,
+ sizeof(debug_entry))) {
+ return false;
+ }
+ for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
+ struct link_map map;
+ if (!dumper_->CopyFromProcess(&map, dumper_->pid(), ptr, sizeof(map)))
+ return false;
+
+ ptr = map.l_next;
+ dso_count++;
+ }
+
+ MDRVA linkmap_rva = MinidumpFileWriter::kInvalidMDRVA;
+ if (dso_count > 0) {
+ // If we have at least one DSO, create an array of MDRawLinkMap
+ // entries in the minidump file.
+ TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
+ if (!linkmap.AllocateArray(dso_count))
+ return false;
+ linkmap_rva = linkmap.location().rva;
+ int idx = 0;
+
+ // Iterate over DSOs and write their information to mini dump
+ for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
+ struct link_map map;
+ if (!dumper_->CopyFromProcess(&map, dumper_->pid(), ptr, sizeof(map)))
+ return false;
+
+ ptr = map.l_next;
+ char filename[257] = { 0 };
+ if (map.l_name) {
+ dumper_->CopyFromProcess(filename, dumper_->pid(), map.l_name,
+ sizeof(filename) - 1);
+ }
+ MDLocationDescriptor location;
+ if (!minidump_writer_.WriteString(filename, 0, &location))
+ return false;
+ MDRawLinkMap entry;
+ entry.name = location.rva;
+ entry.addr = map.l_addr;
+ entry.ld = reinterpret_cast<uintptr_t>(map.l_ld);
+ linkmap.CopyIndex(idx++, &entry);
+ }
+ }
+
+ // Write MD_LINUX_DSO_DEBUG record
+ TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
+ if (!debug.AllocateObjectAndArray(1, dynamic_length))
+ return false;
+ my_memset(debug.get(), 0, sizeof(MDRawDebug));
+ dirent->stream_type = MD_LINUX_DSO_DEBUG;
+ dirent->location = debug.location();
+
+ debug.get()->version = debug_entry.r_version;
+ debug.get()->map = linkmap_rva;
+ debug.get()->dso_count = dso_count;
+ debug.get()->brk = debug_entry.r_brk;
+ debug.get()->ldbase = debug_entry.r_ldbase;
+ debug.get()->dynamic = reinterpret_cast<uintptr_t>(dynamic);
+
+ wasteful_vector<char> dso_debug_data(dumper_->allocator(), dynamic_length);
+ // The passed-in size to the constructor (above) is only a hint.
+ // Must call .resize() to do actual initialization of the elements.
+ dso_debug_data.resize(dynamic_length);
+ dumper_->CopyFromProcess(&dso_debug_data[0], dumper_->pid(), dynamic,
+ dynamic_length);
+ debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length);
+
+ return true;
+ }
+
+ void set_minidump_size_limit(off_t limit) { minidump_size_limit_ = limit; }
+
+ private:
+ void* Alloc(unsigned bytes) {
+ return dumper_->allocator()->Alloc(bytes);
+ }
+
+ pid_t GetCrashThread() const {
+ return dumper_->crash_thread();
+ }
+
+ void NullifyDirectoryEntry(MDRawDirectory* dirent) {
+ dirent->stream_type = 0;
+ dirent->location.data_size = 0;
+ dirent->location.rva = 0;
+ }
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
+ char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
+ static const char vendor_id_name[] = "vendor_id";
+
+ struct CpuInfoEntry {
+ const char* info_name;
+ int value;
+ bool found;
+ } cpu_info_table[] = {
+ { "processor", -1, false },
+#if defined(__i386__) || defined(__x86_64__)
+ { "model", 0, false },
+ { "stepping", 0, false },
+ { "cpu family", 0, false },
+#endif
+ };
+
+ // processor_architecture should always be set, do this first
+ sys_info->processor_architecture =
+#if defined(__mips__)
+# if _MIPS_SIM == _ABIO32
+ MD_CPU_ARCHITECTURE_MIPS;
+# elif _MIPS_SIM == _ABI64
+ MD_CPU_ARCHITECTURE_MIPS64;
+# else
+# error "This mips ABI is currently not supported (n32)"
+#endif
+#elif defined(__i386__)
+ MD_CPU_ARCHITECTURE_X86;
+#else
+ MD_CPU_ARCHITECTURE_AMD64;
+#endif
+
+ const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+
+ {
+ PageAllocator allocator;
+ ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd);
+ const char* field;
+ while (reader->GetNextField(&field)) {
+ bool is_first_entry = true;
+ for (CpuInfoEntry& entry : cpu_info_table) {
+ if (!is_first_entry && entry.found) {
+ // except for the 'processor' field, ignore repeated values.
+ continue;
+ }
+ is_first_entry = false;
+ if (!my_strcmp(field, entry.info_name)) {
+ size_t value_len;
+ const char* value = reader->GetValueAndLen(&value_len);
+ if (value_len == 0)
+ continue;
+
+ uintptr_t val;
+ if (my_read_decimal_ptr(&val, value) == value)
+ continue;
+
+ entry.value = static_cast<int>(val);
+ entry.found = true;
+ }
+ }
+
+ // special case for vendor_id
+ if (!my_strcmp(field, vendor_id_name)) {
+ size_t value_len;
+ const char* value = reader->GetValueAndLen(&value_len);
+ if (value_len > 0)
+ my_strlcpy(vendor_id, value, sizeof(vendor_id));
+ }
+ }
+ sys_close(fd);
+ }
+
+ // make sure we got everything we wanted
+ for (const CpuInfoEntry& entry : cpu_info_table) {
+ if (!entry.found) {
+ return false;
+ }
+ }
+ // cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo,
+ // assuming this is the highest id, change it to the number of CPUs
+ // by adding one.
+ cpu_info_table[0].value++;
+
+ sys_info->number_of_processors = cpu_info_table[0].value;
+#if defined(__i386__) || defined(__x86_64__)
+ sys_info->processor_level = cpu_info_table[3].value;
+ sys_info->processor_revision = cpu_info_table[1].value << 8 |
+ cpu_info_table[2].value;
+#endif
+
+ if (vendor_id[0] != '\0') {
+ my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
+ sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
+ }
+ return true;
+ }
+#elif defined(__arm__) || defined(__aarch64__)
+ bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
+ // The CPUID value is broken up in several entries in /proc/cpuinfo.
+ // This table is used to rebuild it from the entries.
+ const struct CpuIdEntry {
+ const char* field;
+ char format;
+ char bit_lshift;
+ char bit_length;
+ } cpu_id_entries[] = {
+ { "CPU implementer", 'x', 24, 8 },
+ { "CPU variant", 'x', 20, 4 },
+ { "CPU part", 'x', 4, 12 },
+ { "CPU revision", 'd', 0, 4 },
+ };
+
+ // The ELF hwcaps are listed in the "Features" entry as textual tags.
+ // This table is used to rebuild them.
+ const struct CpuFeaturesEntry {
+ const char* tag;
+ uint32_t hwcaps;
+ } cpu_features_entries[] = {
+#if defined(__arm__)
+ { "swp", MD_CPU_ARM_ELF_HWCAP_SWP },
+ { "half", MD_CPU_ARM_ELF_HWCAP_HALF },
+ { "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB },
+ { "26bit", MD_CPU_ARM_ELF_HWCAP_26BIT },
+ { "fastmult", MD_CPU_ARM_ELF_HWCAP_FAST_MULT },
+ { "fpa", MD_CPU_ARM_ELF_HWCAP_FPA },
+ { "vfp", MD_CPU_ARM_ELF_HWCAP_VFP },
+ { "edsp", MD_CPU_ARM_ELF_HWCAP_EDSP },
+ { "java", MD_CPU_ARM_ELF_HWCAP_JAVA },
+ { "iwmmxt", MD_CPU_ARM_ELF_HWCAP_IWMMXT },
+ { "crunch", MD_CPU_ARM_ELF_HWCAP_CRUNCH },
+ { "thumbee", MD_CPU_ARM_ELF_HWCAP_THUMBEE },
+ { "neon", MD_CPU_ARM_ELF_HWCAP_NEON },
+ { "vfpv3", MD_CPU_ARM_ELF_HWCAP_VFPv3 },
+ { "vfpv3d16", MD_CPU_ARM_ELF_HWCAP_VFPv3D16 },
+ { "tls", MD_CPU_ARM_ELF_HWCAP_TLS },
+ { "vfpv4", MD_CPU_ARM_ELF_HWCAP_VFPv4 },
+ { "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA },
+ { "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT },
+ { "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT },
+#elif defined(__aarch64__)
+ // No hwcaps on aarch64.
+#endif
+ };
+
+ // processor_architecture should always be set, do this first
+ sys_info->processor_architecture =
+#if defined(__aarch64__)
+ MD_CPU_ARCHITECTURE_ARM64_OLD;
+#else
+ MD_CPU_ARCHITECTURE_ARM;
+#endif
+
+ // /proc/cpuinfo is not readable under various sandboxed environments
+ // (e.g. Android services with the android:isolatedProcess attribute)
+ // prepare for this by setting default values now, which will be
+ // returned when this happens.
+ //
+ // Note: Bogus values are used to distinguish between failures (to
+ // read /sys and /proc files) and really badly configured kernels.
+ sys_info->number_of_processors = 0;
+ sys_info->processor_level = 1U; // There is no ARMv1
+ sys_info->processor_revision = 42;
+ sys_info->cpu.arm_cpu_info.cpuid = 0;
+ sys_info->cpu.arm_cpu_info.elf_hwcaps = 0;
+
+ // Counting the number of CPUs involves parsing two sysfs files,
+ // because the content of /proc/cpuinfo will only mirror the number
+ // of 'online' cores, and thus will vary with time.
+ // See http://www.kernel.org/doc/Documentation/cputopology.txt
+ {
+ CpuSet cpus_present;
+ CpuSet cpus_possible;
+
+ int fd = sys_open("/sys/devices/system/cpu/present", O_RDONLY, 0);
+ if (fd >= 0) {
+ cpus_present.ParseSysFile(fd);
+ sys_close(fd);
+
+ fd = sys_open("/sys/devices/system/cpu/possible", O_RDONLY, 0);
+ if (fd >= 0) {
+ cpus_possible.ParseSysFile(fd);
+ sys_close(fd);
+
+ cpus_present.IntersectWith(cpus_possible);
+ int cpu_count = cpus_present.GetCount();
+ if (cpu_count > 255)
+ cpu_count = 255;
+ sys_info->number_of_processors = static_cast<uint8_t>(cpu_count);
+ }
+ }
+ }
+
+ // Parse /proc/cpuinfo to reconstruct the CPUID value, as well
+ // as the ELF hwcaps field. For the latter, it would be easier to
+ // read /proc/self/auxv but unfortunately, this file is not always
+ // readable from regular Android applications on later versions
+ // (>= 4.1) of the Android platform.
+ const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
+ if (fd < 0) {
+ // Do not return false here to allow the minidump generation
+ // to happen properly.
+ return true;
+ }
+
+ {
+ PageAllocator allocator;
+ ProcCpuInfoReader* const reader =
+ new(allocator) ProcCpuInfoReader(fd);
+ const char* field;
+ while (reader->GetNextField(&field)) {
+ for (const CpuIdEntry& entry : cpu_id_entries) {
+ if (my_strcmp(entry.field, field) != 0)
+ continue;
+ uintptr_t result = 0;
+ const char* value = reader->GetValue();
+ const char* p = value;
+ if (value[0] == '0' && value[1] == 'x') {
+ p = my_read_hex_ptr(&result, value+2);
+ } else if (entry.format == 'x') {
+ p = my_read_hex_ptr(&result, value);
+ } else {
+ p = my_read_decimal_ptr(&result, value);
+ }
+ if (p == value)
+ continue;
+
+ result &= (1U << entry.bit_length)-1;
+ result <<= entry.bit_lshift;
+ sys_info->cpu.arm_cpu_info.cpuid |=
+ static_cast<uint32_t>(result);
+ }
+#if defined(__arm__)
+ // Get the architecture version from the "Processor" field.
+ // Note that it is also available in the "CPU architecture" field,
+ // however, some existing kernels are misconfigured and will report
+ // invalid values here (e.g. 6, while the CPU is ARMv7-A based).
+ // The "Processor" field doesn't have this issue.
+ if (!my_strcmp(field, "Processor")) {
+ size_t value_len;
+ const char* value = reader->GetValueAndLen(&value_len);
+ // Expected format: <text> (v<level><endian>)
+ // Where <text> is some text like "ARMv7 Processor rev 2"
+ // and <level> is a decimal corresponding to the ARM
+ // architecture number. <endian> is either 'l' or 'b'
+ // and corresponds to the endianess, it is ignored here.
+ while (value_len > 0 && my_isspace(value[value_len-1]))
+ value_len--;
+
+ size_t nn = value_len;
+ while (nn > 0 && value[nn-1] != '(')
+ nn--;
+ if (nn > 0 && value[nn] == 'v') {
+ uintptr_t arch_level = 5;
+ my_read_decimal_ptr(&arch_level, value + nn + 1);
+ sys_info->processor_level = static_cast<uint16_t>(arch_level);
+ }
+ }
+#elif defined(__aarch64__)
+ // The aarch64 architecture does not provide the architecture level
+ // in the Processor field, so we instead check the "CPU architecture"
+ // field.
+ if (!my_strcmp(field, "CPU architecture")) {
+ uintptr_t arch_level = 0;
+ const char* value = reader->GetValue();
+ const char* p = value;
+ p = my_read_decimal_ptr(&arch_level, value);
+ if (p == value)
+ continue;
+ sys_info->processor_level = static_cast<uint16_t>(arch_level);
+ }
+#endif
+ // Rebuild the ELF hwcaps from the 'Features' field.
+ if (!my_strcmp(field, "Features")) {
+ size_t value_len;
+ const char* value = reader->GetValueAndLen(&value_len);
+
+ // Parse each space-separated tag.
+ while (value_len > 0) {
+ const char* tag = value;
+ size_t tag_len = value_len;
+ const char* p = my_strchr(tag, ' ');
+ if (p) {
+ tag_len = static_cast<size_t>(p - tag);
+ value += tag_len + 1;
+ value_len -= tag_len + 1;
+ } else {
+ tag_len = strlen(tag);
+ value_len = 0;
+ }
+ for (const CpuFeaturesEntry& entry : cpu_features_entries) {
+ if (tag_len == strlen(entry.tag) &&
+ !memcmp(tag, entry.tag, tag_len)) {
+ sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry.hwcaps;
+ break;
+ }
+ }
+ }
+ }
+ }
+ sys_close(fd);
+ }
+
+ return true;
+ }
+#else
+# error "Unsupported CPU"
+#endif
+
+ bool WriteFile(MDLocationDescriptor* result, const char* filename) {
+ const int fd = sys_open(filename, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+
+ // We can't stat the files because several of the files that we want to
+ // read are kernel seqfiles, which always have a length of zero. So we have
+ // to read as much as we can into a buffer.
+ static const unsigned kBufSize = 1024 - 2*sizeof(void*);
+ struct Buffers {
+ Buffers* next;
+ size_t len;
+ uint8_t data[kBufSize];
+ } *buffers = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers)));
+ buffers->next = NULL;
+ buffers->len = 0;
+
+ size_t total = 0;
+ for (Buffers* bufptr = buffers;;) {
+ ssize_t r;
+ do {
+ r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
+ } while (r == -1 && errno == EINTR);
+
+ if (r < 1)
+ break;
+
+ total += r;
+ bufptr->len += r;
+ if (bufptr->len == kBufSize) {
+ bufptr->next = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers)));
+ bufptr = bufptr->next;
+ bufptr->next = NULL;
+ bufptr->len = 0;
+ }
+ }
+ sys_close(fd);
+
+ if (!total)
+ return false;
+
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(total))
+ return false;
+ for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
+ // Check for special case of a zero-length buffer. This should only
+ // occur if a file's size happens to be a multiple of the buffer's
+ // size, in which case the final sys_read() will have resulted in
+ // zero bytes being read after the final buffer was just allocated.
+ if (buffers->len == 0) {
+ // This can only occur with final buffer.
+ assert(buffers->next == NULL);
+ continue;
+ }
+ memory.Copy(pos, &buffers->data, buffers->len);
+ pos += buffers->len;
+ }
+ *result = memory.location();
+ return true;
+ }
+
+ bool WriteOSInformation(MDRawSystemInfo* sys_info) {
+#if defined(__ANDROID__)
+ sys_info->platform_id = MD_OS_ANDROID;
+#else
+ sys_info->platform_id = MD_OS_LINUX;
+#endif
+
+ struct utsname uts;
+ if (uname(&uts))
+ return false;
+
+ static const size_t buf_len = 512;
+ char buf[buf_len] = {0};
+ size_t space_left = buf_len - 1;
+ const char* info_table[] = {
+ uts.sysname,
+ uts.release,
+ uts.version,
+ uts.machine,
+ NULL
+ };
+ bool first_item = true;
+ for (const char** cur_info = info_table; *cur_info; cur_info++) {
+ static const char separator[] = " ";
+ size_t separator_len = sizeof(separator) - 1;
+ size_t info_len = my_strlen(*cur_info);
+ if (info_len == 0)
+ continue;
+
+ if (space_left < info_len + (first_item ? 0 : separator_len))
+ break;
+
+ if (!first_item) {
+ my_strlcat(buf, separator, sizeof(buf));
+ space_left -= separator_len;
+ }
+
+ first_item = false;
+ my_strlcat(buf, *cur_info, sizeof(buf));
+ space_left -= info_len;
+ }
+
+ MDLocationDescriptor location;
+ if (!minidump_writer_.WriteString(buf, 0, &location))
+ return false;
+ sys_info->csd_version_rva = location.rva;
+
+ return true;
+ }
+
+ bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
+ const char* filename) {
+ char buf[NAME_MAX];
+ if (!dumper_->BuildProcPath(buf, pid, filename))
+ return false;
+ return WriteFile(result, buf);
+ }
+
+ // Only one of the 2 member variables below should be set to a valid value.
+ const int fd_; // File descriptor where the minidum should be written.
+ const char* path_; // Path to the file where the minidum should be written.
+
+ const ucontext_t* const ucontext_; // also from the signal handler
+#if !defined(__ARM_EABI__) && !defined(__mips__)
+ const google_breakpad::fpstate_t* const float_state_; // ditto
+#endif
+ LinuxDumper* dumper_;
+ MinidumpFileWriter minidump_writer_;
+ off_t minidump_size_limit_;
+ MDLocationDescriptor crashing_thread_context_;
+ // Blocks of memory written to the dump. These are all currently
+ // written while writing the thread list stream, but saved here
+ // so a memory list stream can be written afterwards.
+ wasteful_vector<MDMemoryDescriptor> memory_blocks_;
+ // Additional information about some mappings provided by the caller.
+ const MappingList& mapping_list_;
+ // Additional memory regions to be included in the dump,
+ // provided by the caller.
+ const AppMemoryList& app_memory_list_;
+ // If set, skip recording any threads that do not reference the
+ // mapping containing principal_mapping_address_.
+ bool skip_stacks_if_mapping_unreferenced_;
+ uintptr_t principal_mapping_address_;
+ const MappingInfo* principal_mapping_;
+ // If true, apply stack sanitization to stored stack data.
+ bool sanitize_stacks_;
+};
+
+
+bool WriteMinidumpImpl(const char* minidump_path,
+ int minidump_fd,
+ off_t minidump_size_limit,
+ pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ LinuxPtraceDumper dumper(crashing_process);
+ const ExceptionHandler::CrashContext* context = NULL;
+ if (blob) {
+ if (blob_size != sizeof(ExceptionHandler::CrashContext))
+ return false;
+ context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
+ dumper.SetCrashInfoFromSigInfo(context->siginfo);
+ dumper.set_crash_thread(context->tid);
+ }
+ MinidumpWriter writer(minidump_path, minidump_fd, context, mappings,
+ appmem, skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address, sanitize_stacks, &dumper);
+ // Set desired limit for file size of minidump (-1 means no limit).
+ writer.set_minidump_size_limit(minidump_size_limit);
+ if (!writer.Init())
+ return false;
+ return writer.Dump();
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(minidump_path, -1, -1,
+ crashing_process, blob, blob_size,
+ MappingList(), AppMemoryList(),
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(int minidump_fd, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(NULL, minidump_fd, -1,
+ crashing_process, blob, blob_size,
+ MappingList(), AppMemoryList(),
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(const char* minidump_path, pid_t process,
+ pid_t process_blamed_thread) {
+ LinuxPtraceDumper dumper(process);
+ // MinidumpWriter will set crash address
+ dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED);
+ dumper.set_crash_thread(process_blamed_thread);
+ MappingList mapping_list;
+ AppMemoryList app_memory_list;
+ MinidumpWriter writer(minidump_path, -1, NULL, mapping_list,
+ app_memory_list, false, 0, false, &dumper);
+ if (!writer.Init())
+ return false;
+ return writer.Dump();
+}
+
+bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process,
+ blob, blob_size,
+ mappings, appmem,
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(int minidump_fd, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process,
+ blob, blob_size,
+ mappings, appmem,
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
+ pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit,
+ crashing_process, blob, blob_size,
+ mappings, appmem,
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
+ pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ bool skip_stacks_if_mapping_unreferenced,
+ uintptr_t principal_mapping_address,
+ bool sanitize_stacks) {
+ return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit,
+ crashing_process, blob, blob_size,
+ mappings, appmem,
+ skip_stacks_if_mapping_unreferenced,
+ principal_mapping_address,
+ sanitize_stacks);
+}
+
+bool WriteMinidump(const char* filename,
+ const MappingList& mappings,
+ const AppMemoryList& appmem,
+ LinuxDumper* dumper) {
+ MinidumpWriter writer(filename, -1, NULL, mappings, appmem,
+ false, 0, false, dumper);
+ if (!writer.Init())
+ return false;
+ return writer.Dump();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.h
new file mode 100644
index 0000000000..1d02ec8d7d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2009, 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+#include <list>
+#include <type_traits>
+#include <utility>
+
+#include "linux/minidump_writer/linux_dumper.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+class ExceptionHandler;
+
+#if defined(__aarch64__)
+typedef struct fpsimd_context fpstate_t;
+#elif !defined(__ARM_EABI__) && !defined(__mips__)
+typedef std::remove_pointer<fpregset_t>::type fpstate_t;
+#endif
+
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+ void* ptr;
+ size_t length;
+
+ bool operator==(const struct AppMemory& other) const {
+ return ptr == other.ptr;
+ }
+
+ bool operator==(const void* other) const {
+ return ptr == other;
+ }
+};
+typedef std::list<AppMemory> AppMemoryList;
+
+// Writes a minidump to the filesystem. These functions do not malloc nor use
+// libc functions which may. Thus, it can be used in contexts where the state
+// of the heap may be corrupt.
+// minidump_path: the path to the file to write to. This is opened O_EXCL and
+// fails open fails.
+// crashing_process: the pid of the crashing process. This must be trusted.
+// blob: a blob of data from the crashing process. See exception_handler.h
+// blob_size: the length of |blob|, in bytes
+//
+// Returns true iff successful.
+bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+// Same as above but takes an open file descriptor instead of a path.
+bool WriteMinidump(int minidump_fd, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+
+// Alternate form of WriteMinidump() that works with processes that
+// are not expected to have crashed. If |process_blamed_thread| is
+// meaningful, it will be the one from which a crash signature is
+// extracted. It is not expected that this function will be called
+// from a compromised context, but it is safe to do so.
+bool WriteMinidump(const char* minidump_path, pid_t process,
+ pid_t process_blamed_thread);
+
+// These overloads also allow passing a list of known mappings and
+// a list of additional memory regions to be included in the minidump.
+bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appdata,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+bool WriteMinidump(int minidump_fd, pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appdata,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+
+// These overloads also allow passing a file size limit for the minidump.
+bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
+ pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appdata,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
+ pid_t crashing_process,
+ const void* blob, size_t blob_size,
+ const MappingList& mappings,
+ const AppMemoryList& appdata,
+ bool skip_stacks_if_mapping_unreferenced = false,
+ uintptr_t principal_mapping_address = 0,
+ bool sanitize_stacks = false);
+
+bool WriteMinidump(const char* filename,
+ const MappingList& mappings,
+ const AppMemoryList& appdata,
+ LinuxDumper* dumper);
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest.cc
new file mode 100644
index 0000000000..4e49a4e577
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -0,0 +1,934 @@
+// Copyright (c) 2011 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 <fcntl.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "linux/handler/exception_handler.h"
+#include "linux/minidump_writer/linux_dumper.h"
+#include "linux/minidump_writer/minidump_writer.h"
+#include "linux/minidump_writer/minidump_writer_unittest_utils.h"
+#include "common/linux/breakpad_getcontext.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/file_id.h"
+#include "common/linux/ignore_ret.h"
+#include "common/linux/safe_readlink.h"
+#include "common/scoped_ptr.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/tests/file_utils.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/processor/minidump.h"
+
+using namespace google_breakpad;
+
+namespace {
+
+typedef testing::Test MinidumpWriterTest;
+
+const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
+
+TEST(MinidumpWriterTest, SetupWithPath) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ // Set a non-zero tid to avoid tripping asserts.
+ context.tid = child;
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
+ struct stat st;
+ ASSERT_EQ(0, stat(templ.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+TEST(MinidumpWriterTest, SetupWithFD) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
+ // Set a non-zero tid to avoid tripping asserts.
+ context.tid = child;
+ ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
+ struct stat st;
+ ASSERT_EQ(0, stat(templ.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that mapping info can be specified when writing a minidump,
+// and that it ends up in the module list of the minidump.
+TEST(MinidumpWriterTest, MappingInfo) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t memory_size = sysconf(_SC_PAGESIZE);
+ const char* kMemoryName = "a fake module";
+ const uint8_t kModuleGUID[sizeof(MDGUID)] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+ };
+ const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
+
+ // Get some memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ memory_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+
+ // Add information about the mapped memory.
+ MappingInfo info;
+ info.start_addr = kMemoryAddress;
+ info.size = memory_size;
+ info.offset = 0;
+ info.exec = false;
+ strcpy(info.name, kMemoryName);
+
+ MappingList mappings;
+ AppMemoryList memory_list;
+ MappingEntry mapping;
+ mapping.first = info;
+ mapping.second.assign(std::begin(kModuleGUID), std::end(kModuleGUID));
+ mappings.push_back(mapping);
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
+ mappings, memory_list, false, 0, false));
+
+ // Read the minidump. Load the module list, and ensure that
+ // the mmap'ed |memory| is listed with the given module name
+ // and debug ID.
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* module =
+ module_list->GetModuleForAddress(kMemoryAddress);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(kMemoryAddress, module->base_address());
+ EXPECT_EQ(memory_size, module->size());
+ EXPECT_EQ(kMemoryName, module->code_file());
+ EXPECT_EQ(module_identifier, module->debug_identifier());
+
+ uint32_t len;
+ // These streams are expected to be there
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
+ EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
+
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that minidumping is skipped while writing minidumps if principal mapping
+// is not referenced.
+TEST(MinidumpWriterTest, MinidumpSkippedIfRequested) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+
+ // pass an invalid principal mapping address, which will force
+ // WriteMinidump to not write a minidump.
+ ASSERT_FALSE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
+ true, static_cast<uintptr_t>(0x0102030405060708ull),
+ false));
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that minidumping is skipped while writing minidumps if principal mapping
+// is not referenced.
+TEST(MinidumpWriterTest, MinidumpStacksSkippedIfRequested) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+
+ // Create a thread that does not return, and only references libc (not the
+ // current executable). This thread should not be captured in the minidump.
+ pthread_t thread;
+ pthread_attr_t thread_attributes;
+ pthread_attr_init(&thread_attributes);
+ pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ pthread_create(&thread, &thread_attributes,
+ reinterpret_cast<void* (*)(void*)>(&sigsuspend), &sigset);
+
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+
+ // Pass an invalid principal mapping address, which will force
+ // WriteMinidump to not dump any thread stacks.
+ ASSERT_TRUE(WriteMinidump(
+ templ.c_str(), child, &context, sizeof(context), true,
+ reinterpret_cast<uintptr_t>(google_breakpad::WriteFile), false));
+
+ // Read the minidump. And ensure that thread memory was dumped only for the
+ // main thread.
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpThreadList *threads = minidump.GetThreadList();
+ int threads_with_stacks = 0;
+ for (unsigned int i = 0; i < threads->thread_count(); ++i) {
+ MinidumpThread *thread = threads->GetThreadAtIndex(i);
+ if (thread->GetMemory()) {
+ ++threads_with_stacks;
+ }
+ }
+#if defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER)
+ ASSERT_GE(threads_with_stacks, 1);
+#else
+ ASSERT_EQ(threads_with_stacks, 1);
+#endif
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that stacks can be sanitized while writing minidumps.
+TEST(MinidumpWriterTest, StacksAreSanitizedIfRequested) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ // pass an invalid principal mapping address, which will force
+ // WriteMinidump to not dump any thread stacks.
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
+ false, 0, true));
+
+ // Read the minidump. And ensure that thread memory contains a defaced value.
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ const uintptr_t defaced =
+#if defined(__LP64__)
+ 0x0defaced0defaced;
+#else
+ 0x0defaced;
+#endif
+ MinidumpThreadList *threads = minidump.GetThreadList();
+ for (unsigned int i = 0; i < threads->thread_count(); ++i) {
+ MinidumpThread *thread = threads->GetThreadAtIndex(i);
+ MinidumpMemoryRegion *mem = thread->GetMemory();
+ ASSERT_TRUE(mem != nullptr);
+ uint32_t sz = mem->GetSize();
+ const uint8_t *data = mem->GetMemory();
+ ASSERT_TRUE(memmem(data, sz, &defaced, sizeof(defaced)) != nullptr);
+ }
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that a binary with a longer-than-usual build id note
+// makes its way all the way through to the minidump unscathed.
+// The linux_client_unittest is linked with an explicit --build-id
+// in Makefile.am.
+TEST(MinidumpWriterTest, BuildIDLong) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ const string dump_path = temp_dir.path() + kMDWriterUnitTestFileName;
+
+ EXPECT_TRUE(WriteMinidump(dump_path.c_str(),
+ child, &context, sizeof(context)));
+ close(fds[1]);
+
+ // Read the minidump. Load the module list, and ensure that
+ // the main module has the correct debug id and code id.
+ Minidump minidump(dump_path);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* module = module_list->GetMainModule();
+ ASSERT_TRUE(module);
+ const string module_identifier = "030201000504070608090A0B0C0D0E0F0";
+ // This is passed explicitly to the linker in Makefile.am
+ const string build_id =
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
+ EXPECT_EQ(module_identifier, module->debug_identifier());
+ EXPECT_EQ(build_id, module->code_identifier());
+
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that mapping info can be specified, and that it overrides
+// existing mappings that are wholly contained within the specified
+// range.
+TEST(MinidumpWriterTest, MappingInfoContained) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const int32_t memory_size = sysconf(_SC_PAGESIZE);
+ const char* kMemoryName = "a fake module";
+ const uint8_t kModuleGUID[sizeof(MDGUID)] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+ };
+ const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
+
+ // mmap a file
+ AutoTempDir temp_dir;
+ string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
+ int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
+ ASSERT_NE(-1, fd);
+ unlink(tempfile.c_str());
+ // fill with zeros
+ google_breakpad::scoped_array<char> buffer(new char[memory_size]);
+ memset(buffer.get(), 0, memory_size);
+ ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
+ lseek(fd, 0, SEEK_SET);
+
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ memory_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ fd,
+ 0));
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+ close(fd);
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+ context.tid = 1;
+
+ string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
+
+ // Add information about the mapped memory. Report it as being larger than
+ // it actually is.
+ MappingInfo info;
+ info.start_addr = kMemoryAddress - memory_size;
+ info.size = memory_size * 3;
+ info.offset = 0;
+ info.exec = false;
+ strcpy(info.name, kMemoryName);
+
+ MappingList mappings;
+ AppMemoryList memory_list;
+ MappingEntry mapping;
+ mapping.first = info;
+ mapping.second.assign(std::begin(kModuleGUID), std::end(kModuleGUID));
+ mappings.push_back(mapping);
+ ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
+ mappings, memory_list));
+
+ // Read the minidump. Load the module list, and ensure that
+ // the mmap'ed |memory| is listed with the given module name
+ // and debug ID.
+ Minidump minidump(dumpfile);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* module =
+ module_list->GetModuleForAddress(kMemoryAddress);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(info.start_addr, module->base_address());
+ EXPECT_EQ(info.size, module->size());
+ EXPECT_EQ(kMemoryName, module->code_file());
+ EXPECT_EQ(module_identifier, module->debug_identifier());
+
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+TEST(MinidumpWriterTest, DeletedBinary) {
+ const string kNumberOfThreadsArgument = "1";
+ const string helper_path(GetHelperBinary());
+ if (helper_path.empty()) {
+ FAIL() << "Couldn't find helper binary";
+ exit(1);
+ }
+
+ // Copy binary to a temp file.
+ AutoTempDir temp_dir;
+ string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
+ ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
+ << "Failed to copy " << helper_path << " to " << binpath;
+ ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
+
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ // In child process.
+ close(fds[0]);
+
+ // Pass the pipe fd and the number of threads as arguments.
+ char pipe_fd_string[8];
+ sprintf(pipe_fd_string, "%d", fds[1]);
+ execl(binpath.c_str(),
+ binpath.c_str(),
+ pipe_fd_string,
+ kNumberOfThreadsArgument.c_str(),
+ NULL);
+ }
+ close(fds[1]);
+ // Wait for the child process to signal that it's ready.
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fds[0];
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(pfd.revents & POLLIN);
+ uint8_t junk;
+ const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
+ close(fds[0]);
+
+ // Child is ready now.
+ // Unlink the test binary.
+ unlink(binpath.c_str());
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ // Set a non-zero tid to avoid tripping asserts.
+ context.tid = child_pid;
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
+ sizeof(context)));
+ kill(child_pid, SIGKILL);
+
+ struct stat st;
+ ASSERT_EQ(0, stat(templ.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ // Check that the main module filename is correct.
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* module = module_list->GetMainModule();
+ EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
+ // Check that the file ID is correct.
+ FileID fileid(helper_path.c_str());
+ PageAllocator allocator;
+ wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize);
+ EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ string module_identifier(identifier_string);
+ // Strip out dashes
+ size_t pos;
+ while ((pos = module_identifier.find('-')) != string::npos) {
+ module_identifier.erase(pos, 1);
+ }
+ // And append a zero, because module IDs include an "age" field
+ // which is always zero on Linux.
+ module_identifier += "0";
+ EXPECT_EQ(module_identifier, module->debug_identifier());
+
+ IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
+}
+
+// Test that an additional memory region can be added to the minidump.
+TEST(MinidumpWriterTest, AdditionalMemory) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ uint8_t* memory = new uint8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (uint32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+
+ // This needs a valid context for minidump writing to work, but getting
+ // a useful one from the child is too much work, so just use one from
+ // the parent since the child is just a forked copy anyway.
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ unlink(templ.c_str());
+
+ MappingList mappings;
+ AppMemoryList memory_list;
+
+ // Add the memory region to the list of memory to be included.
+ AppMemory app_memory;
+ app_memory.ptr = memory;
+ app_memory.length = kMemorySize;
+ memory_list.push_back(app_memory);
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
+ mappings, memory_list));
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemoryAddress, region->GetBase());
+ EXPECT_EQ(kMemorySize, region->GetSize());
+
+ // Verify memory contents.
+ EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
+
+ delete[] memory;
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that an invalid thread stack pointer still results in a minidump.
+TEST(MinidumpWriterTest, InvalidStackPointer) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit_group);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+
+ // This needs a valid context for minidump writing to work, but getting
+ // a useful one from the child is too much work, so just use one from
+ // the parent since the child is just a forked copy anyway.
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ // Fake the child's stack pointer for its crashing thread. NOTE: This must
+ // be an invalid memory address for the child process (stack or otherwise).
+ // Try 1MB below the current stack.
+ uintptr_t invalid_stack_pointer =
+ reinterpret_cast<uintptr_t>(&context) - 1024*1024;
+#if defined(__i386)
+ context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
+#elif defined(__x86_64)
+ context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
+#elif defined(__ARM_EABI__)
+ context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
+#elif defined(__aarch64__)
+ context.context.uc_mcontext.sp = invalid_stack_pointer;
+#elif defined(__mips__)
+ context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
+ invalid_stack_pointer;
+#else
+# error "This code has not been ported to your platform yet."
+#endif
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + kMDWriterUnitTestFileName;
+ // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
+ // presented with an invalid stack pointer.
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(templ);
+ ASSERT_TRUE(minidump.Read());
+
+ // TODO(ted.mielczarek,mkrebs): Enable this part of the test once
+ // https://breakpad.appspot.com/413002/ is committed.
+#if 0
+ // Make sure there's a thread without a stack. NOTE: It's okay if
+ // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
+ // region problem".
+ MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(dump_thread_list);
+ bool found_empty_stack = false;
+ for (int i = 0; i < dump_thread_list->thread_count(); i++) {
+ MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
+ ASSERT_TRUE(thread->thread() != NULL);
+ // When the stack size is zero bytes, GetMemory() returns NULL.
+ if (thread->GetMemory() == NULL) {
+ found_empty_stack = true;
+ break;
+ }
+ }
+ // NOTE: If you fail this, first make sure that "invalid_stack_pointer"
+ // above is indeed set to an invalid address.
+ ASSERT_TRUE(found_empty_stack);
+#endif
+
+ close(fds[1]);
+ IGNORE_EINTR(waitpid(child, nullptr, 0));
+}
+
+// Test that limiting the size of the minidump works.
+TEST(MinidumpWriterTest, MinidumpSizeLimit) {
+ static const int kNumberOfThreadsInHelperProgram = 40;
+
+ char number_of_threads_arg[3];
+ sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
+
+ string helper_path(GetHelperBinary());
+ if (helper_path.empty()) {
+ FAIL() << "Couldn't find helper binary";
+ exit(1);
+ }
+
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ // In child process.
+ close(fds[0]);
+
+ // Pass the pipe fd and the number of threads as arguments.
+ char pipe_fd_string[8];
+ sprintf(pipe_fd_string, "%d", fds[1]);
+ execl(helper_path.c_str(),
+ helper_path.c_str(),
+ pipe_fd_string,
+ number_of_threads_arg,
+ NULL);
+ }
+ close(fds[1]);
+
+ // Wait for all child threads to indicate that they have started
+ for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fds[0];
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
+ ASSERT_EQ(1, r);
+ ASSERT_TRUE(pfd.revents & POLLIN);
+ uint8_t junk;
+ ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
+ static_cast<ssize_t>(sizeof(junk)));
+ }
+ close(fds[0]);
+
+ // There is a race here because we may stop a child thread before
+ // it is actually running the busy loop. Empirically this sleep
+ // is sufficient to avoid the race.
+ usleep(100000);
+
+ // Child and its threads are ready now.
+
+
+ off_t normal_file_size;
+ int total_normal_stack_size = 0;
+ AutoTempDir temp_dir;
+
+ // First, write a minidump with no size limit.
+ {
+ string normal_dump = temp_dir.path() +
+ "/minidump-writer-unittest.dmp";
+ ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+ normal_file_size = st.st_size;
+
+ Minidump minidump(normal_dump);
+ ASSERT_TRUE(minidump.Read());
+ MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(dump_thread_list);
+ for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
+ MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
+ ASSERT_TRUE(thread->thread() != NULL);
+ // When the stack size is zero bytes, GetMemory() returns NULL.
+ MinidumpMemoryRegion* memory = thread->GetMemory();
+ ASSERT_TRUE(memory != NULL);
+ total_normal_stack_size += memory->GetSize();
+ }
+ }
+
+ // Second, write a minidump with a size limit big enough to not trigger
+ // anything.
+ {
+ // Set size limit arbitrarily 1MB larger than the normal file size -- such
+ // that the limiting code will not kick in.
+ const off_t minidump_size_limit = normal_file_size + 1024*1024;
+
+ string same_dump = temp_dir.path() +
+ "/minidump-writer-unittest-same.dmp";
+ ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(same_dump.c_str(), &st));
+ // Make sure limiting wasn't actually triggered. NOTE: If you fail this,
+ // first make sure that "minidump_size_limit" above is indeed set to a
+ // large enough value -- the limit-checking code in minidump_writer.cc
+ // does just a rough estimate.
+ ASSERT_EQ(normal_file_size, st.st_size);
+ }
+
+ // Third, write a minidump with a size limit small enough to be triggered.
+ {
+ // Set size limit to some arbitrary amount, such that the limiting code
+ // will kick in. The equation used to set this value was determined by
+ // simply reversing the size-limit logic a little bit in order to pick a
+ // size we know will trigger it. The definition of
+ // kLimitAverageThreadStackLength here was copied from class
+ // MinidumpWriter in minidump_writer.cc.
+ static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
+ off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
+ kLimitAverageThreadStackLength;
+ // If, in reality, each of the threads' stack is *smaller* than
+ // kLimitAverageThreadStackLength, the normal file size could very well be
+ // smaller than the arbitrary limit that was just set. In that case,
+ // either of these numbers should trigger the size-limiting code, but we
+ // might as well pick the smallest.
+ if (normal_file_size < minidump_size_limit)
+ minidump_size_limit = normal_file_size;
+
+ string limit_dump = temp_dir.path() +
+ "/minidump-writer-unittest-limit.dmp";
+ ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
+ child_pid, NULL, 0,
+ MappingList(), AppMemoryList()));
+ struct stat st;
+ ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
+ ASSERT_GT(st.st_size, 0);
+ // Make sure the file size is at least smaller than the original. If this
+ // fails because it's the same size, then the size-limit logic didn't kick
+ // in like it was supposed to.
+ EXPECT_LT(st.st_size, normal_file_size);
+
+ Minidump minidump(limit_dump);
+ ASSERT_TRUE(minidump.Read());
+ MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(dump_thread_list);
+ int total_limit_stack_size = 0;
+ for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
+ MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
+ ASSERT_TRUE(thread->thread() != NULL);
+ // When the stack size is zero bytes, GetMemory() returns NULL.
+ MinidumpMemoryRegion* memory = thread->GetMemory();
+ ASSERT_TRUE(memory != NULL);
+ total_limit_stack_size += memory->GetSize();
+ }
+
+ // Make sure stack size shrunk by at least 1KB per extra thread. The
+ // definition of kLimitBaseThreadCount here was copied from class
+ // MinidumpWriter in minidump_writer.cc.
+ // Note: The 1KB is arbitrary, and assumes that the thread stacks are big
+ // enough to shrink by that much. For example, if each thread stack was
+ // originally only 2KB, the current size-limit logic wouldn't actually
+ // shrink them because that's the size to which it tries to shrink. If
+ // you fail this part of the test due to something like that, the test
+ // logic should probably be improved to account for your situation.
+ const unsigned kLimitBaseThreadCount = 20;
+ const unsigned kMinPerExtraThreadStackReduction = 1024;
+ const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
+ kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
+ EXPECT_LT(total_limit_stack_size,
+ total_normal_stack_size - min_expected_reduction);
+ }
+
+ // Kill the helper program.
+ kill(child_pid, SIGKILL);
+ IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
+}
+
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.cc
new file mode 100644
index 0000000000..3e27be73cc
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 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_writer_unittest_utils.cc:
+// Shared routines used by unittests under linux/minidump_writer.
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include "linux/minidump_writer/minidump_writer_unittest_utils.h"
+#include "common/linux/safe_readlink.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+string GetHelperBinary() {
+ string helper_path;
+ char *bindir = getenv("bindir");
+ if (bindir) {
+ helper_path = string(bindir) + "/";
+ } else {
+ // Locate helper binary next to the current binary.
+ char self_path[PATH_MAX];
+ if (!SafeReadLink("/proc/self/exe", self_path)) {
+ return "";
+ }
+ helper_path = string(self_path);
+ size_t pos = helper_path.rfind('/');
+ if (pos == string::npos) {
+ return "";
+ }
+ helper_path.erase(pos + 1);
+ }
+
+ helper_path += "linux_dumper_unittest_helper";
+
+ return helper_path;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.h
new file mode 100644
index 0000000000..bc7b7c83f4
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer_unittest_utils.h
@@ -0,0 +1,49 @@
+// 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.
+
+// minidump_writer_unittest_utils.h:
+// Shared routines used by unittests under linux/minidump_writer.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
+
+#include <string>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+// Returns the full path to linux_dumper_unittest_helper. The full path is
+// discovered either by using the environment variable "bindir" or by using
+// the location of the main module of the currently running process.
+string GetHelperBinary();
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader.h
new file mode 100644
index 0000000000..94a637fc15
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader.h
@@ -0,0 +1,130 @@
+// 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.
+
+#ifndef CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
+#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "linux/minidump_writer/line_reader.h"
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for reading /proc/cpuinfo without using fopen/fgets or other
+// functions which may allocate memory.
+class ProcCpuInfoReader {
+public:
+ ProcCpuInfoReader(int fd)
+ : line_reader_(fd), pop_count_(-1) {
+ }
+
+ // Return the next field name, or NULL in case of EOF.
+ // field: (output) Pointer to zero-terminated field name.
+ // Returns true on success, or false on EOF or error (line too long).
+ bool GetNextField(const char** field) {
+ for (;;) {
+ const char* line;
+ unsigned line_len;
+
+ // Try to read next line.
+ if (pop_count_ >= 0) {
+ line_reader_.PopLine(pop_count_);
+ pop_count_ = -1;
+ }
+
+ if (!line_reader_.GetNextLine(&line, &line_len))
+ return false;
+
+ pop_count_ = static_cast<int>(line_len);
+
+ const char* line_end = line + line_len;
+
+ // Expected format: <field-name> <space>+ ':' <space> <value>
+ // Note that:
+ // - empty lines happen.
+ // - <field-name> can contain spaces.
+ // - some fields have an empty <value>
+ char* sep = static_cast<char*>(my_memchr(line, ':', line_len));
+ if (sep == NULL)
+ continue;
+
+ // Record the value. Skip leading space after the column to get
+ // its start.
+ const char* val = sep+1;
+ while (val < line_end && my_isspace(*val))
+ val++;
+
+ value_ = val;
+ value_len_ = static_cast<size_t>(line_end - val);
+
+ // Remove trailing spaces before the column to properly 0-terminate
+ // the field name.
+ while (sep > line && my_isspace(sep[-1]))
+ sep--;
+
+ if (sep == line)
+ continue;
+
+ // zero-terminate field name.
+ *sep = '\0';
+
+ *field = line;
+ return true;
+ }
+ }
+
+ // Return the field value. This must be called after a succesful
+ // call to GetNextField().
+ const char* GetValue() {
+ assert(value_);
+ return value_;
+ }
+
+ // Same as GetValue(), but also returns the length in characters of
+ // the value.
+ const char* GetValueAndLen(size_t* length) {
+ assert(value_);
+ *length = value_len_;
+ return value_;
+ }
+
+private:
+ LineReader line_reader_;
+ int pop_count_;
+ const char* value_;
+ size_t value_len_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
new file mode 100644
index 0000000000..26fe9e3a38
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
@@ -0,0 +1,199 @@
+// 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.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "linux/minidump_writer/proc_cpuinfo_reader.h"
+#include "breakpad_googletest_includes.h"
+#include "common/linux/tests/auto_testfile.h"
+
+using namespace google_breakpad;
+
+#if !defined(__ANDROID__)
+#define TEMPDIR "/tmp"
+#else
+#define TEMPDIR "/data/local/tmp"
+#endif
+
+
+namespace {
+
+typedef testing::Test ProcCpuInfoReaderTest;
+
+class ScopedTestFile : public AutoTestFile {
+public:
+ explicit ScopedTestFile(const char* text)
+ : AutoTestFile("proc_cpuinfo_reader", text) {
+ }
+};
+
+}
+
+TEST(ProcCpuInfoReaderTest, EmptyFile) {
+ ScopedTestFile file("");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char *field;
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, OneLineTerminated) {
+ ScopedTestFile file("foo : bar\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char *field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, OneLine) {
+ ScopedTestFile file("foo : bar");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char *field;
+ size_t value_len;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len));
+ ASSERT_EQ(3U, value_len);
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) {
+ ScopedTestFile file("foo : bar\nzoo : tut\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("zoo", field);
+ ASSERT_STREQ("tut", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, SkipMalformedLine) {
+ ScopedTestFile file("this line should have a column\nfoo : bar\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) {
+ ScopedTestFile file("\n\nfoo : bar\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, SkipEmptyField) {
+ ScopedTestFile file(" : bar\nzoo : tut\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("zoo", field);
+ ASSERT_STREQ("tut", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) {
+ ScopedTestFile file("foo : bar\n\n\nfoo : bar\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ ASSERT_STREQ("bar", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, FieldWithSpaces) {
+ ScopedTestFile file("foo bar : zoo\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo bar", field);
+ ASSERT_STREQ("zoo", reader.GetValue());
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
+
+TEST(ProcCpuInfoReaderTest, EmptyValue) {
+ ScopedTestFile file("foo :\n");
+ ASSERT_TRUE(file.IsOk());
+ ProcCpuInfoReader reader(file.GetFd());
+
+ const char* field;
+ ASSERT_TRUE(reader.GetNextField(&field));
+ ASSERT_STREQ("foo", field);
+ size_t value_len;
+ ASSERT_STREQ("", reader.GetValueAndLen(&value_len));
+ ASSERT_EQ(0U, value_len);
+
+ ASSERT_FALSE(reader.GetNextField(&field));
+}
diff --git a/toolkit/crashreporter/breakpad-client/linux/moz.build b/toolkit/crashreporter/breakpad-client/linux/moz.build
new file mode 100644
index 0000000000..ac4fb1e230
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/linux/moz.build
@@ -0,0 +1,47 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'crash_generation/crash_generation_client.cc',
+ 'crash_generation/crash_generation_server.cc',
+ 'dump_writer_common/thread_info.cc',
+ 'dump_writer_common/ucontext_reader.cc',
+ 'handler/exception_handler.cc',
+ 'handler/guid_generator.cc',
+ 'handler/minidump_descriptor.cc',
+ 'log/log.cc',
+ 'microdump_writer/microdump_writer.cc',
+ 'minidump_writer/linux_dumper.cc',
+ 'minidump_writer/linux_ptrace_dumper.cc',
+]
+
+if CONFIG["CC_TYPE"] == "gcc":
+ # Workaround for an ICE in the hazard detection GCC plug-in, see
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1756505#c7
+ SOURCES += ['minidump_writer/minidump_writer.cc']
+else:
+ UNIFIED_SOURCES += ['minidump_writer/minidump_writer.cc']
+
+# On Linux we override the guid_creator.h header and use our own instead
+if CONFIG['OS_TARGET'] == 'Linux' or CONFIG['OS_TARGET'] == 'Android':
+ DEFINES['COMMON_LINUX_GUID_CREATOR_H__'] = 1
+
+if CONFIG['OS_TARGET'] == 'Android':
+ DEFINES['ANDROID_NDK_MAJOR_VERSION'] = CONFIG['ANDROID_NDK_MAJOR_VERSION']
+ DEFINES['ANDROID_NDK_MINOR_VERSION'] = CONFIG['ANDROID_NDK_MINOR_VERSION']
+ LOCAL_INCLUDES += [
+ '/toolkit/crashreporter/google-breakpad/src/common/android/include',
+ ]
+
+FINAL_LIBRARY = 'breakpad_client'
+
+include('/toolkit/crashreporter/crashreporter.mozbuild')
+
+if CONFIG['MOZ_PHC']:
+ DEFINES['MOZ_PHC'] = True
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ CXXFLAGS += ['-Wno-error=stack-protector']
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/client_info.h b/toolkit/crashreporter/breakpad-client/mac/crash_generation/client_info.h
new file mode 100644
index 0000000000..92301036b6
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/client_info.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.
+
+#ifndef CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
+#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
+
+namespace google_breakpad {
+
+class ClientInfo {
+ public:
+ ClientInfo(pid_t pid, task_t task) : pid_(pid), task_(task) {}
+
+ pid_t pid() const { return pid_; }
+ task_t task() const { return task_; }
+
+ private:
+ pid_t pid_;
+ task_t task_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.cc b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.cc
new file mode 100644
index 0000000000..bdbf43aaca
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.cc
@@ -0,0 +1,74 @@
+// 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 "mac/crash_generation/crash_generation_client.h"
+
+#include "mac/crash_generation/crash_generation_server.h"
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+bool CrashGenerationClient::RequestDumpForException(
+ int exception_type,
+ int exception_code,
+ int64_t exception_subcode,
+ mach_port_t crashing_thread,
+ mach_port_t crashing_task) {
+ // The server will send a message to this port indicating that it
+ // has finished its work.
+ ReceivePort acknowledge_port;
+
+ MachSendMessage message(kDumpRequestMessage);
+ message.AddDescriptor(crashing_task); // crashing task
+ message.AddDescriptor(crashing_thread); // crashing thread
+ message.AddDescriptor(MACH_PORT_NULL); // handler thread
+ message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
+
+ ExceptionInfo info;
+ info.exception_type = exception_type;
+ info.exception_code = exception_code;
+ info.exception_subcode = exception_subcode;
+ info.child_pid = getpid();
+
+ message.SetData(&info, sizeof(info));
+
+ kern_return_t result = sender_.SendMessage(message, MACH_MSG_TIMEOUT_NONE);
+ if (result != KERN_SUCCESS)
+ return false;
+
+ // Give the server slightly longer to reply since it has to
+ // inspect this task and write the minidump.
+ MachReceiveMessage acknowledge_message;
+ result = acknowledge_port.WaitForMessage(&acknowledge_message,
+ MACH_MSG_TIMEOUT_NONE);
+
+ return result == KERN_SUCCESS;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.h b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.h
new file mode 100644
index 0000000000..9bcd79af7f
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_client.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+class CrashGenerationClient {
+ public:
+ explicit CrashGenerationClient(const char* mach_port_name)
+ : sender_(mach_port_name) {
+ }
+
+ // Request the crash server to generate a dump.
+ //
+ // Return true if the dump was successful; false otherwise.
+ bool RequestDumpForException(int exception_type,
+ int exception_code,
+ int64_t exception_subcode,
+ mach_port_t crashing_thread,
+ mach_port_t crashing_task);
+
+ bool RequestDump() {
+ return RequestDumpForException(0, 0, 0, MACH_PORT_NULL, mach_task_self());
+ }
+
+ private:
+ MachPortSender sender_;
+
+ // Prevent copy construction and assignment.
+ CrashGenerationClient(const CrashGenerationClient&);
+ CrashGenerationClient& operator=(const CrashGenerationClient&);
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.cc
new file mode 100644
index 0000000000..8cbc012caa
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.cc
@@ -0,0 +1,164 @@
+// 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 "mac/crash_generation/crash_generation_server.h"
+
+#include <pthread.h>
+
+#include "mac/crash_generation/client_info.h"
+#include "mac/handler/minidump_generator.h"
+#include "common/mac/scoped_task_suspend-inl.h"
+
+namespace google_breakpad {
+
+CrashGenerationServer::CrashGenerationServer(
+ const char *mach_port_name,
+ FilterCallback filter,
+ void *filter_context,
+ OnClientDumpRequestCallback dump_callback,
+ void *dump_context,
+ OnClientExitingCallback exit_callback,
+ void *exit_context,
+ bool generate_dumps,
+ const std::string &dump_path)
+ : filter_(filter),
+ filter_context_(filter_context),
+ dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ generate_dumps_(generate_dumps),
+ dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
+ started_(false),
+ receive_port_(mach_port_name),
+ mach_port_name_(mach_port_name) {
+}
+
+CrashGenerationServer::~CrashGenerationServer() {
+ if (started_)
+ Stop();
+}
+
+bool CrashGenerationServer::Start() {
+ int thread_create_result = pthread_create(&server_thread_, NULL,
+ &WaitForMessages, this);
+ started_ = thread_create_result == 0;
+ return started_;
+}
+
+bool CrashGenerationServer::Stop() {
+ if (!started_)
+ return false;
+
+ // Send a quit message to the background thread, and then join it.
+ MachPortSender sender(mach_port_name_.c_str());
+ MachSendMessage quit_message(kQuitMessage);
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
+ kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
+ if (result == KERN_SUCCESS) {
+ int thread_join_result = pthread_join(server_thread_, NULL);
+ started_ = thread_join_result != 0;
+ }
+
+ return !started_;
+}
+
+// static
+void *CrashGenerationServer::WaitForMessages(void *server) {
+ pthread_setname_np("Breakpad CrashGenerationServer");
+
+ CrashGenerationServer *self =
+ reinterpret_cast<CrashGenerationServer*>(server);
+ while (self->WaitForOneMessage()) {}
+ return NULL;
+}
+
+bool CrashGenerationServer::WaitForOneMessage() {
+ MachReceiveMessage message;
+ kern_return_t result = receive_port_.WaitForMessage(&message,
+ MACH_MSG_TIMEOUT_NONE);
+ if (result == KERN_SUCCESS) {
+ switch (message.GetMessageID()) {
+ case kDumpRequestMessage: {
+ ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
+
+ mach_port_t remote_task = message.GetTranslatedPort(0);
+ mach_port_t crashing_thread = message.GetTranslatedPort(1);
+ mach_port_t handler_thread = message.GetTranslatedPort(2);
+ mach_port_t ack_port = message.GetTranslatedPort(3);
+ ClientInfo client(info.child_pid, remote_task);
+
+ bool result;
+ std::string dump_path;
+ if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
+ ScopedTaskSuspend suspend(remote_task);
+
+ MinidumpGenerator generator(remote_task, handler_thread);
+ dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
+
+ if (info.exception_type && info.exception_code) {
+ generator.SetExceptionInformation(info.exception_type,
+ info.exception_code,
+ info.exception_subcode,
+ crashing_thread);
+ }
+ result = generator.Write(dump_path.c_str());
+ } else {
+ result = true;
+ }
+
+ if (result && dump_callback_) {
+ dump_callback_(dump_context_, client, dump_path);
+ }
+
+ // TODO(ted): support a way for the client to send additional data,
+ // perhaps with a callback so users of the server can read the data
+ // themselves?
+
+ if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
+ MachPortSender sender(ack_port);
+ MachSendMessage ack_message(kAcknowledgementMessage);
+ sender.SendMessage(ack_message, MACH_MSG_TIMEOUT_NONE);
+ }
+
+ if (result && exit_callback_) {
+ exit_callback_(exit_context_, client);
+ }
+ break;
+ }
+ case kQuitMessage:
+ return false;
+ }
+ } else { // result != KERN_SUCCESS
+ return false;
+ }
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.h b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.h
new file mode 100644
index 0000000000..d0b39f3acf
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/crash_generation_server.h
@@ -0,0 +1,151 @@
+// 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.
+
+#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+class ClientInfo;
+
+// Messages the server can read via its mach port
+enum {
+ kDumpRequestMessage = 1,
+ kAcknowledgementMessage = 2,
+ kQuitMessage = 3
+};
+
+// Exception details sent by the client when requesting a dump.
+struct ExceptionInfo {
+ int32_t exception_type;
+ int32_t exception_code;
+ int64_t exception_subcode;
+ int32_t child_pid;
+};
+
+class CrashGenerationServer {
+ public:
+ // WARNING: callbacks may be invoked on a different thread
+ // than that which creates the CrashGenerationServer. They must
+ // be thread safe.
+ typedef void (*OnClientDumpRequestCallback)(void *context,
+ const ClientInfo &client_info,
+ const std::string &file_path);
+
+ typedef void (*OnClientExitingCallback)(void *context,
+ const ClientInfo &client_info);
+ // If a FilterCallback returns false, the dump will not be written.
+ typedef bool (*FilterCallback)(void *context);
+
+ // Create an instance with the given parameters.
+ //
+ // mach_port_name: Named server port to listen on.
+ // filter: Callback for a client to cancel writing a dump.
+ // filter_context: Context for the filter callback.
+ // dump_callback: Callback for a client crash dump request.
+ // dump_context: Context for client crash dump request callback.
+ // exit_callback: Callback for client process exit.
+ // exit_context: Context for client exit callback.
+ // generate_dumps: Whether to automatically generate dumps.
+ // Client code of this class might want to generate dumps explicitly
+ // in the crash dump request callback. In that case, false can be
+ // passed for this parameter.
+ // dump_path: Path for generating dumps; required only if true is
+ // passed for generateDumps parameter; NULL can be passed otherwise.
+ CrashGenerationServer(const char *mach_port_name,
+ FilterCallback filter,
+ void *filter_context,
+ OnClientDumpRequestCallback dump_callback,
+ void *dump_context,
+ OnClientExitingCallback exit_callback,
+ void *exit_context,
+ bool generate_dumps,
+ const std::string &dump_path);
+
+ ~CrashGenerationServer();
+
+ // Perform initialization steps needed to start listening to clients.
+ //
+ // Return true if initialization is successful; false otherwise.
+ bool Start();
+
+ // Stop the server.
+ bool Stop();
+
+ private:
+ // Return a unique filename at which a minidump can be written.
+ bool MakeMinidumpFilename(std::string &outFilename);
+
+ // Loop reading client messages and responding to them until
+ // a quit message is received.
+ static void *WaitForMessages(void *server);
+
+ // Wait for a single client message and respond to it. Returns false
+ // if a quit message was received or if an error occurred.
+ bool WaitForOneMessage();
+
+ FilterCallback filter_;
+ void *filter_context_;
+
+ OnClientDumpRequestCallback dump_callback_;
+ void *dump_context_;
+
+ OnClientExitingCallback exit_callback_;
+ void *exit_context_;
+
+ bool generate_dumps_;
+
+ std::string dump_dir_;
+
+ bool started_;
+
+ // The mach port that receives requests to dump from child processes.
+ ReceivePort receive_port_;
+
+ // The name of the mach port. Stored so the Stop method can message
+ // the background thread to shut it down.
+ std::string mach_port_name_;
+
+ // The thread that waits on the receive port.
+ pthread_t server_thread_;
+
+ // Disable copy constructor and operator=.
+ CrashGenerationServer(const CrashGenerationServer&);
+ CrashGenerationServer& operator=(const CrashGenerationServer&);
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
diff --git a/toolkit/crashreporter/breakpad-client/mac/crash_generation/moz.build b/toolkit/crashreporter/breakpad-client/mac/crash_generation/moz.build
new file mode 100644
index 0000000000..731670a4f1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/crash_generation/moz.build
@@ -0,0 +1,18 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'crash_generation_client.cc',
+ 'crash_generation_server.cc',
+]
+
+FINAL_LIBRARY = 'breakpad_client'
+
+LOCAL_INCLUDES += [
+ '/ipc/chromium/src',
+ '/toolkit/crashreporter/breakpad-client',
+ '/toolkit/crashreporter/google-breakpad/src',
+]
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.cc b/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.cc
new file mode 100644
index 0000000000..3492b823da
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.cc
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * This file was copied from libc/gen/nlist.c from Darwin's source code
+ * The version of nlist used as a base is from 10.5.2, libc-498
+ * http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
+ *
+ * The full tarball is at:
+ * http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
+ *
+ * I've modified it to be compatible with 64-bit images.
+*/
+
+#include "breakpad_nlist_64.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <fcntl.h>
+#include <mach-o/nlist.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach/mach.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <TargetConditionals.h>
+#include <unistd.h>
+
+/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
+/*
+ * Header prepended to each a.out file.
+ */
+struct exec {
+ unsigned short a_machtype; /* machine type */
+ unsigned short a_magic; /* magic number */
+ unsigned long a_text; /* size of text segment */
+ unsigned long a_data; /* size of initialized data */
+ unsigned long a_bss; /* size of uninitialized data */
+ unsigned long a_syms; /* size of symbol table */
+ unsigned long a_entry; /* entry point */
+ unsigned long a_trsize; /* size of text relocation */
+ unsigned long a_drsize; /* size of data relocation */
+};
+
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+
+#define N_BADMAG(x) \
+ (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC)
+#define N_TXTOFF(x) \
+ ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec))
+#define N_SYMOFF(x) \
+ (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
+
+// Traits structs for specializing function templates to handle
+// 32-bit/64-bit Mach-O files.
+template<typename T>
+struct MachBits {};
+
+typedef struct nlist nlist32;
+typedef struct nlist_64 nlist64;
+
+template<>
+struct MachBits<nlist32> {
+ typedef mach_header mach_header_type;
+ typedef uint32_t word_type;
+ static const uint32_t magic = MH_MAGIC;
+};
+
+template<>
+struct MachBits<nlist64> {
+ typedef mach_header_64 mach_header_type;
+ typedef uint64_t word_type;
+ static const uint32_t magic = MH_MAGIC_64;
+};
+
+template<typename nlist_type>
+int
+__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
+ cpu_type_t cpu_type);
+
+/*
+ * nlist - retreive attributes from name list (string table version)
+ */
+
+template <typename nlist_type>
+int breakpad_nlist_common(const char *name,
+ nlist_type *list,
+ const char **symbolNames,
+ cpu_type_t cpu_type) {
+ int fd = open(name, O_RDONLY, 0);
+ if (fd < 0)
+ return -1;
+ int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
+ close(fd);
+ return n;
+}
+
+int breakpad_nlist(const char *name,
+ struct nlist *list,
+ const char **symbolNames,
+ cpu_type_t cpu_type) {
+ return breakpad_nlist_common(name, list, symbolNames, cpu_type);
+}
+
+int breakpad_nlist(const char *name,
+ struct nlist_64 *list,
+ const char **symbolNames,
+ cpu_type_t cpu_type) {
+ return breakpad_nlist_common(name, list, symbolNames, cpu_type);
+}
+
+/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
+
+template<typename nlist_type>
+int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
+ cpu_type_t cpu_type) {
+ typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
+ typedef typename MachBits<nlist_type>::word_type word_type;
+
+ const uint32_t magic = MachBits<nlist_type>::magic;
+
+ int maxlen = 500;
+ int nreq = 0;
+ for (nlist_type* q = list;
+ symbolNames[q-list] && symbolNames[q-list][0];
+ q++, nreq++) {
+
+ q->n_type = 0;
+ q->n_value = 0;
+ q->n_desc = 0;
+ q->n_sect = 0;
+ q->n_un.n_strx = 0;
+ }
+
+ struct exec buf;
+ if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
+ (N_BADMAG(buf) && *((uint32_t *)&buf) != magic &&
+ CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
+ /* The following is the big-endian ppc64 check */
+ (*((uint32_t*)&buf)) != FAT_MAGIC)) {
+ return -1;
+ }
+
+ /* Deal with fat file if necessary */
+ unsigned arch_offset = 0;
+ if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC ||
+ /* The following is the big-endian ppc64 check */
+ *((unsigned int *)&buf) == FAT_MAGIC) {
+ /* Read in the fat header */
+ struct fat_header fh;
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ return -1;
+ }
+ if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
+ return -1;
+ }
+
+ /* Convert fat_narchs to host byte order */
+ fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch);
+
+ /* Read in the fat archs */
+ struct fat_arch *fat_archs =
+ (struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
+ if (fat_archs == NULL) {
+ return -1;
+ }
+ if (read(fd, (char *)fat_archs,
+ sizeof(struct fat_arch) * fh.nfat_arch) !=
+ (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) {
+ free(fat_archs);
+ return -1;
+ }
+
+ /*
+ * Convert archs to host byte ordering (a constraint of
+ * cpusubtype_getbestarch()
+ */
+ for (unsigned i = 0; i < fh.nfat_arch; i++) {
+ fat_archs[i].cputype =
+ CFSwapInt32BigToHost(fat_archs[i].cputype);
+ fat_archs[i].cpusubtype =
+ CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
+ fat_archs[i].offset =
+ CFSwapInt32BigToHost(fat_archs[i].offset);
+ fat_archs[i].size =
+ CFSwapInt32BigToHost(fat_archs[i].size);
+ fat_archs[i].align =
+ CFSwapInt32BigToHost(fat_archs[i].align);
+ }
+
+ struct fat_arch *fap = NULL;
+ for (unsigned i = 0; i < fh.nfat_arch; i++) {
+ if (fat_archs[i].cputype == cpu_type) {
+ fap = &fat_archs[i];
+ break;
+ }
+ }
+
+ if (!fap) {
+ free(fat_archs);
+ return -1;
+ }
+ arch_offset = fap->offset;
+ free(fat_archs);
+
+ /* Read in the beginning of the architecture-specific file */
+ if (lseek(fd, arch_offset, SEEK_SET) == -1) {
+ return -1;
+ }
+ if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
+ return -1;
+ }
+ }
+
+ off_t sa; /* symbol address */
+ off_t ss; /* start of strings */
+ register_t n;
+ if (*((unsigned int *)&buf) == magic) {
+ if (lseek(fd, arch_offset, SEEK_SET) == -1) {
+ return -1;
+ }
+ mach_header_type mh;
+ if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
+ return -1;
+ }
+
+ struct load_command *load_commands =
+ (struct load_command *)malloc(mh.sizeofcmds);
+ if (load_commands == NULL) {
+ return -1;
+ }
+ if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
+ (ssize_t)mh.sizeofcmds) {
+ free(load_commands);
+ return -1;
+ }
+ struct symtab_command *stp = NULL;
+ struct load_command *lcp = load_commands;
+ // iterate through all load commands, looking for
+ // LC_SYMTAB load command
+ for (uint32_t i = 0; i < mh.ncmds; i++) {
+ if (lcp->cmdsize % sizeof(word_type) != 0 ||
+ lcp->cmdsize <= 0 ||
+ (char *)lcp + lcp->cmdsize >
+ (char *)load_commands + mh.sizeofcmds) {
+ free(load_commands);
+ return -1;
+ }
+ if (lcp->cmd == LC_SYMTAB) {
+ if (lcp->cmdsize !=
+ sizeof(struct symtab_command)) {
+ free(load_commands);
+ return -1;
+ }
+ stp = (struct symtab_command *)lcp;
+ break;
+ }
+ lcp = (struct load_command *)
+ ((char *)lcp + lcp->cmdsize);
+ }
+ if (stp == NULL) {
+ free(load_commands);
+ return -1;
+ }
+ // sa points to the beginning of the symbol table
+ sa = stp->symoff + arch_offset;
+ // ss points to the beginning of the string table
+ ss = stp->stroff + arch_offset;
+ // n is the number of bytes in the symbol table
+ // each symbol table entry is an nlist structure
+ n = stp->nsyms * sizeof(nlist_type);
+ free(load_commands);
+ } else {
+ sa = N_SYMOFF(buf) + arch_offset;
+ ss = sa + buf.a_syms + arch_offset;
+ n = buf.a_syms;
+ }
+
+ if (lseek(fd, sa, SEEK_SET) == -1) {
+ return -1;
+ }
+
+ // the algorithm here is to read the nlist entries in m-sized
+ // chunks into q. q is then iterated over. for each entry in q,
+ // use the string table index(q->n_un.n_strx) to read the symbol
+ // name, then scan the nlist entries passed in by the user(via p),
+ // and look for a match
+ while (n) {
+ nlist_type space[BUFSIZ/sizeof (nlist_type)];
+ register_t m = sizeof (space);
+
+ if (n < m)
+ m = n;
+ if (read(fd, (char *)space, m) != m)
+ break;
+ n -= m;
+ off_t savpos = lseek(fd, 0, SEEK_CUR);
+ if (savpos == -1) {
+ return -1;
+ }
+ for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
+ char nambuf[BUFSIZ];
+
+ if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
+ continue;
+
+ // seek to the location in the binary where the symbol
+ // name is stored & read it into memory
+ if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
+ return -1;
+ }
+ if (read(fd, nambuf, maxlen+1) == -1) {
+ return -1;
+ }
+ const char *s2 = nambuf;
+ for (nlist_type *p = list;
+ symbolNames[p-list] && symbolNames[p-list][0];
+ p++) {
+ // get the symbol name the user has passed in that
+ // corresponds to the nlist entry that we're looking at
+ const char *s1 = symbolNames[p - list];
+ while (*s1) {
+ if (*s1++ != *s2++)
+ goto cont;
+ }
+ if (*s2)
+ goto cont;
+
+ p->n_value = q->n_value;
+ p->n_type = q->n_type;
+ p->n_desc = q->n_desc;
+ p->n_sect = q->n_sect;
+ p->n_un.n_strx = q->n_un.n_strx;
+ if (--nreq == 0)
+ return nreq;
+
+ break;
+ cont: ;
+ }
+ }
+ if (lseek(fd, savpos, SEEK_SET) == -1) {
+ return -1;
+ }
+ }
+ return nreq;
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.h b/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.h
new file mode 100644
index 0000000000..e8e2e0834e
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/breakpad_nlist_64.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2008, 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.
+
+// breakpad_nlist.h
+//
+// This file is meant to provide a header for clients of the modified
+// nlist function implemented to work on 64-bit.
+
+#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
+#define CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
+
+#include <mach/machine.h>
+
+int breakpad_nlist(const char *name,
+ struct nlist *list,
+ const char **symbolNames,
+ cpu_type_t cpu_type);
+int breakpad_nlist(const char *name,
+ struct nlist_64 *list,
+ const char **symbolNames,
+ cpu_type_t cpu_type);
+
+#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc
new file mode 100644
index 0000000000..855580a071
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc
@@ -0,0 +1,625 @@
+// 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.
+
+#include "mac/handler/dynamic_images.h"
+
+extern "C" { // needed to compile on Leopard
+ #include <mach-o/nlist.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+}
+
+#include <assert.h>
+#include <AvailabilityMacros.h>
+#include <dlfcn.h>
+#include <mach/task_info.h>
+#include <sys/sysctl.h>
+#include <TargetConditionals.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "breakpad_nlist_64.h"
+
+#if !TARGET_OS_IPHONE
+#include <CoreServices/CoreServices.h>
+#endif // !TARGET_OS_IPHONE
+
+namespace google_breakpad {
+
+using std::string;
+using std::vector;
+
+//==============================================================================
+// Returns the size of the memory region containing |address| and the
+// number of bytes from |address| to the end of the region.
+// We potentially, will extend the size of the original
+// region by the size of the following region if it's contiguous with the
+// first in order to handle cases when we're reading strings and they
+// straddle two vm regions.
+//
+static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
+ const uint64_t address,
+ mach_vm_size_t *size_to_end) {
+ mach_vm_address_t region_base = (mach_vm_address_t)address;
+ mach_vm_size_t region_size;
+ natural_t nesting_level = 0;
+ vm_region_submap_info_64 submap_info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ // Get information about the vm region containing |address|
+ vm_region_recurse_info_t region_info;
+ region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
+
+ kern_return_t result =
+ mach_vm_region_recurse(target_task,
+ &region_base,
+ &region_size,
+ &nesting_level,
+ region_info,
+ &info_count);
+
+ if (result == KERN_SUCCESS) {
+ // Get distance from |address| to the end of this region
+ *size_to_end = region_base + region_size -(mach_vm_address_t)address;
+
+ // If we want to handle strings as long as 4096 characters we may need
+ // to check if there's a vm region immediately following the first one.
+ // If so, we need to extend |*size_to_end| to go all the way to the end
+ // of the second region.
+ if (*size_to_end < 4096) {
+ // Second region starts where the first one ends
+ mach_vm_address_t region_base2 =
+ (mach_vm_address_t)(region_base + region_size);
+ mach_vm_size_t region_size2;
+
+ // Get information about the following vm region
+ result =
+ mach_vm_region_recurse(target_task,
+ &region_base2,
+ &region_size2,
+ &nesting_level,
+ region_info,
+ &info_count);
+
+ // Extend region_size to go all the way to the end of the 2nd region
+ if (result == KERN_SUCCESS
+ && region_base2 == region_base + region_size) {
+ region_size += region_size2;
+ }
+ }
+
+ *size_to_end = region_base + region_size -(mach_vm_address_t)address;
+ } else {
+ region_size = 0;
+ *size_to_end = 0;
+ }
+
+ return region_size;
+}
+
+#define kMaxStringLength 8192
+//==============================================================================
+// Reads a NULL-terminated string from another task.
+//
+// Warning! This will not read any strings longer than kMaxStringLength-1
+//
+string ReadTaskString(task_port_t target_task,
+ const uint64_t address) {
+ // The problem is we don't know how much to read until we know how long
+ // the string is. And we don't know how long the string is, until we've read
+ // the memory! So, we'll try to read kMaxStringLength bytes
+ // (or as many bytes as we can until we reach the end of the vm region).
+ mach_vm_size_t size_to_end;
+ GetMemoryRegionSize(target_task, address, &size_to_end);
+
+ if (size_to_end > 0) {
+ mach_vm_size_t size_to_read =
+ size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
+
+ vector<uint8_t> bytes;
+ if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) !=
+ KERN_SUCCESS)
+ return string();
+
+ return string(reinterpret_cast<const char*>(&bytes[0]));
+ }
+
+ return string();
+}
+
+//==============================================================================
+// Reads an address range from another task. The bytes read will be returned
+// in bytes, which will be resized as necessary.
+kern_return_t ReadTaskMemory(task_port_t target_task,
+ const uint64_t address,
+ size_t length,
+ vector<uint8_t> &bytes) {
+ int systemPageSize = getpagesize();
+
+ // use the negative of the page size for the mask to find the page address
+ mach_vm_address_t page_address = address & (-systemPageSize);
+
+ mach_vm_address_t last_page_address =
+ (address + length + (systemPageSize - 1)) & (-systemPageSize);
+
+ mach_vm_size_t page_size = last_page_address - page_address;
+ uint8_t* local_start;
+ uint32_t local_length;
+
+ kern_return_t r = mach_vm_read(target_task,
+ page_address,
+ page_size,
+ reinterpret_cast<vm_offset_t*>(&local_start),
+ &local_length);
+
+ if (r != KERN_SUCCESS)
+ return r;
+
+ bytes.resize(length);
+ memcpy(&bytes[0],
+ &local_start[(mach_vm_address_t)address - page_address],
+ length);
+ mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
+ return KERN_SUCCESS;
+}
+
+#pragma mark -
+
+//==============================================================================
+// Traits structs for specializing function templates to handle
+// 32-bit/64-bit Mach-O files.
+struct MachO32 {
+ typedef mach_header mach_header_type;
+ typedef segment_command mach_segment_command_type;
+ typedef dyld_image_info32 dyld_image_info;
+ typedef dyld_all_image_infos32 dyld_all_image_infos;
+ typedef section mach_section_type;
+ typedef struct nlist nlist_type;
+ static const uint32_t magic = MH_MAGIC;
+ static const uint32_t segment_load_command = LC_SEGMENT;
+};
+
+struct MachO64 {
+ typedef mach_header_64 mach_header_type;
+ typedef segment_command_64 mach_segment_command_type;
+ typedef dyld_image_info64 dyld_image_info;
+ typedef dyld_all_image_infos64 dyld_all_image_infos;
+ typedef section_64 mach_section_type;
+ typedef struct nlist_64 nlist_type;
+ static const uint32_t magic = MH_MAGIC_64;
+ static const uint32_t segment_load_command = LC_SEGMENT_64;
+};
+
+template<typename MachBits>
+bool FindTextSection(DynamicImage& image) {
+ typedef typename MachBits::mach_header_type mach_header_type;
+ typedef typename MachBits::mach_segment_command_type
+ mach_segment_command_type;
+ typedef typename MachBits::mach_section_type mach_section_type;
+
+ const mach_header_type* header =
+ reinterpret_cast<const mach_header_type*>(&image.header_[0]);
+
+ if(header->magic != MachBits::magic) {
+ return false;
+ }
+
+ bool is_in_shared_cache = ((header->flags & MH_SHAREDCACHE) != 0);
+ if (is_in_shared_cache) {
+ image.slide_ = image.shared_cache_slide_;
+ }
+
+ const struct load_command *cmd =
+ reinterpret_cast<const struct load_command *>(header + 1);
+
+ bool retval = false;
+
+ uint32_t num_data_sections = 0;
+ const mach_section_type *data_sections = NULL;
+ uint32_t num_data_dirty_sections = 0;
+ const mach_section_type *data_dirty_sections = NULL;
+ bool found_text_section = false;
+ bool found_dylib_id_command = false;
+ for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
+ if (!data_sections) {
+ if (cmd->cmd == MachBits::segment_load_command) {
+ const mach_segment_command_type *seg =
+ reinterpret_cast<const mach_segment_command_type *>(cmd);
+
+ if (!strcmp(seg->segname, "__DATA")) {
+ num_data_sections = seg->nsects;
+ data_sections = reinterpret_cast<const mach_section_type *>(seg + 1);
+ }
+ }
+ }
+
+ if (!data_dirty_sections) {
+ if (cmd->cmd == MachBits::segment_load_command) {
+ const mach_segment_command_type *seg =
+ reinterpret_cast<const mach_segment_command_type *>(cmd);
+
+ if (!strcmp(seg->segname, "__DATA_DIRTY")) {
+ num_data_dirty_sections = seg->nsects;
+ data_dirty_sections =
+ reinterpret_cast<const mach_section_type *>(seg + 1);
+ }
+ }
+ }
+
+ if (!found_text_section) {
+ if (cmd->cmd == MachBits::segment_load_command) {
+ const mach_segment_command_type *seg =
+ reinterpret_cast<const mach_segment_command_type *>(cmd);
+
+ if (!is_in_shared_cache) {
+ if (seg->fileoff == 0 && seg->filesize != 0) {
+ image.slide_ =
+ (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
+ }
+ }
+
+ if (!strcmp(seg->segname, "__TEXT")) {
+ image.vmaddr_ = static_cast<mach_vm_address_t>(seg->vmaddr);
+ image.vmsize_ = static_cast<mach_vm_size_t>(seg->vmsize);
+ found_text_section = true;
+ }
+ }
+ }
+
+ if (!found_dylib_id_command) {
+ if (cmd->cmd == LC_ID_DYLIB) {
+ const struct dylib_command *dc =
+ reinterpret_cast<const struct dylib_command *>(cmd);
+
+ image.version_ = dc->dylib.current_version;
+ found_dylib_id_command = true;
+ }
+ }
+
+ if (found_dylib_id_command && found_text_section &&
+ data_sections && data_dirty_sections) {
+ break;
+ }
+
+ cmd = reinterpret_cast<const struct load_command *>
+ (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
+ }
+
+ if (found_dylib_id_command && found_text_section) {
+ retval = true;
+ }
+
+ // The __DYLD,__crash_info section may not be accessible in child process
+ // modules that aren't dyld or in the dyld shared cache.
+ if (image.GetIsDyld() || is_in_shared_cache) {
+ for (unsigned int i = 0; i < num_data_sections; ++i) {
+ if (!strcmp(data_sections[i].sectname, "__crash_info")) {
+ ReadTaskMemory(image.task_,
+ data_sections[i].addr + image.slide_,
+ data_sections[i].size,
+ image.crash_info_);
+ return retval;
+ }
+ }
+ // __crash_info might be in the __DATA_DIRTY segment.
+ for (unsigned int i = 0; i < num_data_dirty_sections; ++i) {
+ if (!strcmp(data_dirty_sections[i].sectname, "__crash_info")) {
+ ReadTaskMemory(image.task_,
+ data_dirty_sections[i].addr + image.slide_,
+ data_dirty_sections[i].size,
+ image.crash_info_);
+ return retval;
+ }
+ }
+ }
+
+ return retval;
+}
+
+//==============================================================================
+// Initializes vmaddr_, vmsize_, and slide_
+void DynamicImage::CalculateMemoryAndVersionInfo() {
+ // unless we can process the header, ensure that calls to
+ // IsValid() will return false
+ vmaddr_ = 0;
+ vmsize_ = 0;
+ slide_ = 0;
+ version_ = 0;
+
+ // The function template above does all the real work.
+ if (Is64Bit())
+ FindTextSection<MachO64>(*this);
+ else
+ FindTextSection<MachO32>(*this);
+}
+
+//==============================================================================
+// The helper function template abstracts the 32/64-bit differences.
+template<typename MachBits>
+uint32_t GetFileTypeFromHeader(DynamicImage& image) {
+ typedef typename MachBits::mach_header_type mach_header_type;
+
+ const mach_header_type* header =
+ reinterpret_cast<const mach_header_type*>(&image.header_[0]);
+ return header->filetype;
+}
+
+uint32_t DynamicImage::GetFileType() {
+ if (Is64Bit())
+ return GetFileTypeFromHeader<MachO64>(*this);
+
+ return GetFileTypeFromHeader<MachO32>(*this);
+}
+
+#pragma mark -
+
+//==============================================================================
+// Loads information about dynamically loaded code in the given task.
+DynamicImages::DynamicImages(mach_port_t task)
+ : task_(task),
+ cpu_type_(DetermineTaskCPUType(task)),
+ image_list_() {
+ ReadImageInfoForTask();
+}
+
+template<typename MachBits>
+static uint64_t LookupSymbol(const char* symbol_name,
+ const char* filename,
+ cpu_type_t cpu_type) {
+ typedef typename MachBits::nlist_type nlist_type;
+
+ nlist_type symbol_info[8] = {};
+ const char *symbolNames[2] = { symbol_name, "\0" };
+ nlist_type &list = symbol_info[0];
+ int invalidEntriesCount = breakpad_nlist(filename,
+ &list,
+ symbolNames,
+ cpu_type);
+
+ if(invalidEntriesCount != 0) {
+ return 0;
+ }
+
+ assert(list.n_value);
+ return list.n_value;
+}
+
+uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
+ &count) != KERN_SUCCESS) {
+ return 0;
+ }
+
+ return (uint64_t)task_dyld_info.all_image_info_addr;
+}
+
+//==============================================================================
+// This code was written using dyld_debug.c (from Darwin) as a guide.
+
+template<typename MachBits>
+void ReadOneImageInfo(DynamicImages& images, uint64_t image_address,
+ uint64_t file_path_address, uint64_t file_mod_date,
+ uint64_t shared_cache_slide, bool is_dyld) {
+ typedef typename MachBits::mach_header_type mach_header_type;
+
+ // First read just the mach_header from the image in the task.
+ vector<uint8_t> mach_header_bytes;
+ if (ReadTaskMemory(images.task_,
+ image_address,
+ sizeof(mach_header_type),
+ mach_header_bytes) != KERN_SUCCESS) {
+ return; // bail on this dynamic image
+ }
+
+ mach_header_type *header =
+ reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
+ if (header->magic != MachBits::magic) {
+ return;
+ }
+
+ cpu_subtype_t cpusubtype = (header->cpusubtype & ~CPU_SUBTYPE_MASK);
+
+ // Now determine the total amount necessary to read the header
+ // plus all of the load commands.
+ size_t header_size = sizeof(mach_header_type) + header->sizeofcmds;
+
+ if (ReadTaskMemory(images.task_,
+ image_address,
+ header_size,
+ mach_header_bytes) != KERN_SUCCESS) {
+ return;
+ }
+
+ // Read the file name from the task's memory space.
+ string file_path;
+ if (file_path_address) {
+ // Although we're reading kMaxStringLength bytes, it's copied in the
+ // the DynamicImage constructor below with the correct string length,
+ // so it's not really wasting memory.
+ file_path = ReadTaskString(images.task_, file_path_address);
+ }
+
+ // Create an object representing this image and add it to our list.
+ DynamicImage *new_image;
+ new_image = new DynamicImage(&mach_header_bytes[0],
+ header_size,
+ image_address,
+ file_path,
+ static_cast<uintptr_t>(file_mod_date),
+ images.task_,
+ images.cpu_type_,
+ cpusubtype,
+ shared_cache_slide,
+ is_dyld);
+
+ if (new_image->IsValid()) {
+ images.image_list_.push_back(DynamicImageRef(new_image));
+ } else {
+ delete new_image;
+ }
+}
+
+template<typename MachBits>
+void ReadImageInfo(DynamicImages& images,
+ uint64_t image_list_address) {
+ typedef typename MachBits::dyld_image_info dyld_image_info;
+ typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos;
+
+ // Read the structure inside of dyld that contains information about
+ // loaded images. We're reading from the desired task's address space.
+
+ // Here we make the assumption that dyld loaded at the same address in
+ // the crashed process vs. this one. This is an assumption made in
+ // "dyld_debug.c" and is said to be nearly always valid.
+ vector<uint8_t> dyld_all_info_bytes;
+ if (ReadTaskMemory(images.task_,
+ image_list_address,
+ sizeof(dyld_all_image_infos),
+ dyld_all_info_bytes) != KERN_SUCCESS) {
+ return;
+ }
+
+ dyld_all_image_infos *dyldInfo =
+ reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
+
+ // number of loaded images
+ int count = dyldInfo->infoArrayCount;
+
+ // Read an array of dyld_image_info structures each containing
+ // information about a loaded image.
+ vector<uint8_t> dyld_info_array_bytes;
+ if (ReadTaskMemory(images.task_,
+ dyldInfo->infoArray,
+ count * sizeof(dyld_image_info),
+ dyld_info_array_bytes) != KERN_SUCCESS) {
+ return;
+ }
+
+ dyld_image_info *infoArray =
+ reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
+ // Add room for dyld at the end
+ images.image_list_.reserve(count + 1);
+
+ for (int i = 0; i < count; ++i) {
+ dyld_image_info &info = infoArray[i];
+ ReadOneImageInfo<MachBits>(images, info.load_address_,
+ info.file_path_, info.file_mod_date_,
+ dyldInfo->sharedCacheSlide,
+ /* is_dyld */ false);
+ }
+
+ // Add an image for dyld itself. It doesn't appear in the standard list of
+ // modules.
+ uint64_t dyld_address = (uint64_t) dyldInfo->dyldImageLoadAddress;
+ if (dyld_address) {
+ ReadOneImageInfo<MachBits>(images, dyld_address,
+ (uint64_t) dyldInfo->dyldPath,
+ /* file_mod_date */ 0,
+ dyldInfo->sharedCacheSlide,
+ /* is_dyld */ true);
+ }
+
+ // sorts based on loading address
+ sort(images.image_list_.begin(), images.image_list_.end());
+ // remove duplicates - this happens in certain strange cases
+ // You can see it in DashboardClient when Google Gadgets plugin
+ // is installed. Apple's crash reporter log and gdb "info shared"
+ // both show the same library multiple times at the same address
+
+ vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(),
+ images.image_list_.end());
+ images.image_list_.erase(it, images.image_list_.end());
+}
+
+void DynamicImages::ReadImageInfoForTask() {
+ uint64_t imageList = GetDyldAllImageInfosPointer();
+
+ if (imageList) {
+ if (Is64Bit())
+ ReadImageInfo<MachO64>(*this, imageList);
+ else
+ ReadImageInfo<MachO32>(*this, imageList);
+ }
+}
+
+//==============================================================================
+DynamicImage *DynamicImages::GetExecutableImage() {
+ int executable_index = GetExecutableImageIndex();
+
+ if (executable_index >= 0) {
+ return GetImage(executable_index);
+ }
+
+ return NULL;
+}
+
+//==============================================================================
+// returns -1 if failure to find executable
+int DynamicImages::GetExecutableImageIndex() {
+ int image_count = GetImageCount();
+
+ for (int i = 0; i < image_count; ++i) {
+ DynamicImage *image = GetImage(i);
+ if (image->GetFileType() == MH_EXECUTE) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//==============================================================================
+// static
+cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
+ if (task == mach_task_self())
+ return GetNativeCPUType();
+
+ int mib[CTL_MAXNAME];
+ size_t mibLen = CTL_MAXNAME;
+ int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
+ if (err == 0) {
+ assert(mibLen < CTL_MAXNAME);
+ pid_for_task(task, &mib[mibLen]);
+ mibLen += 1;
+
+ cpu_type_t cpu_type;
+ size_t cpuTypeSize = sizeof(cpu_type);
+ sysctl(mib, static_cast<u_int>(mibLen), &cpu_type, &cpuTypeSize, 0, 0);
+ return cpu_type;
+ }
+
+ return GetNativeCPUType();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h
new file mode 100644
index 0000000000..e225c00b88
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h
@@ -0,0 +1,386 @@
+// 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.
+
+// dynamic_images.h
+//
+// Implements most of the function of the dyld API, but allowing an
+// arbitrary task to be introspected, unlike the dyld API which
+// only allows operation on the current task. The current implementation
+// is limited to use by 32-bit tasks.
+
+#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
+#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
+
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "mach_vm_compat.h"
+
+namespace google_breakpad {
+
+using std::string;
+using std::vector;
+
+//==============================================================================
+// The memory layout of this struct matches the dyld_image_info struct
+// defined in "dyld_gdb.h" in the darwin source.
+typedef struct dyld_image_info32 {
+ uint32_t load_address_; // struct mach_header*
+ uint32_t file_path_; // char*
+ uint32_t file_mod_date_;
+} dyld_image_info32;
+
+typedef struct dyld_image_info64 {
+ uint64_t load_address_; // struct mach_header*
+ uint64_t file_path_; // char*
+ uint64_t file_mod_date_;
+} dyld_image_info64;
+
+//==============================================================================
+// This is as defined in "dyld_gdb.h" in the darwin source.
+// _dyld_all_image_infos (in dyld) is a structure of this type
+// which will be used to determine which dynamic code has been loaded.
+typedef struct dyld_all_image_infos32 {
+ uint32_t version; // == 1 in Mac OS X 10.4
+ uint32_t infoArrayCount;
+ uint32_t infoArray; // const struct dyld_image_info*
+ uint32_t notification;
+ bool processDetachedFromSharedRegion;
+ // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later
+ const struct mach_header* dyldImageLoadAddress;
+ uint32_t padding[14];
+ // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later
+ uint32_t sharedCacheSlide;
+ uint32_t padding1[6];
+ // Only in version 15 (macOS 10.12, iOS 10.0) and later
+ const char* dyldPath;
+} dyld_all_image_infos32;
+
+typedef struct dyld_all_image_infos64 {
+ uint32_t version; // == 1 in Mac OS X 10.4
+ uint32_t infoArrayCount;
+ uint64_t infoArray; // const struct dyld_image_info*
+ uint64_t notification;
+ bool processDetachedFromSharedRegion;
+ // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later
+ const struct mach_header_64* dyldImageLoadAddress;
+ uint64_t padding[14];
+ // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later
+ uint64_t sharedCacheSlide;
+ uint64_t padding1[4];
+ // Only in version 15 (macOS 10.12, iOS 10.0) and later
+ const char* dyldPath;
+} dyld_all_image_infos64;
+
+// some typedefs to isolate 64/32 bit differences
+#ifdef __LP64__
+typedef mach_header_64 breakpad_mach_header;
+typedef segment_command_64 breakpad_mach_segment_command;
+#else
+typedef mach_header breakpad_mach_header;
+typedef segment_command breakpad_mach_segment_command;
+#endif
+
+// Bit in mach_header.flags that indicates whether or not the image is in the
+// dyld shared cache. The dyld shared cache is a single image into which
+// commonly used system dylibs and frameworks are incorporated. dyld maps it
+// into every process at load time. The component images all have the same
+// slide.
+#define MH_SHAREDCACHE 0x80000000
+
+// Helper functions to deal with 32-bit/64-bit Mach-O differences.
+class DynamicImage;
+template<typename MachBits>
+bool FindTextSection(DynamicImage& image);
+
+template<typename MachBits>
+uint32_t GetFileTypeFromHeader(DynamicImage& image);
+
+//==============================================================================
+// Represents a single dynamically loaded mach-o image
+class DynamicImage {
+ public:
+ DynamicImage(uint8_t *header, // data is copied
+ size_t header_size, // includes load commands
+ uint64_t load_address,
+ string file_path,
+ uintptr_t image_mod_date,
+ mach_port_t task,
+ cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ ptrdiff_t shared_cache_slide,
+ bool is_dyld)
+ : header_(header, header + header_size),
+ header_size_(header_size),
+ load_address_(load_address),
+ vmaddr_(0),
+ vmsize_(0),
+ slide_(0),
+ crash_info_(),
+ version_(0),
+ file_path_(file_path),
+ file_mod_date_(image_mod_date),
+ is_dyld_(is_dyld),
+ task_(task),
+ cpu_type_(cpu_type),
+ cpu_subtype_(cpu_subtype),
+ shared_cache_slide_(shared_cache_slide) {
+ CalculateMemoryAndVersionInfo();
+ }
+
+ // Size of mach_header plus load commands
+ size_t GetHeaderSize() const {return header_.size();}
+
+ // Full path to mach-o binary
+ string GetFilePath() {return file_path_;}
+
+ uint64_t GetModDate() const {return file_mod_date_;}
+
+ // Actual address where the image was loaded
+ uint64_t GetLoadAddress() const {return load_address_;}
+
+ // Address where the image should be loaded
+ mach_vm_address_t GetVMAddr() const {return vmaddr_;}
+
+ bool GetInDyldSharedCache()
+ {return (shared_cache_slide_ && (slide_ == shared_cache_slide_));}
+
+ bool GetIsDyld() {return is_dyld_;}
+
+ // Difference between GetLoadAddress() and GetVMAddr()
+ ptrdiff_t GetVMAddrSlide() const {return slide_;}
+
+ // Size of the image
+ mach_vm_size_t GetVMSize() const {return vmsize_;}
+
+ // Returns the address of the locally cached __DATA,__crash_info section.
+ // The vector will be empty if the image doesn't have a __crash_info
+ // section. But even if the vector isn't empty, its contents may be "empty"
+ // of useful data (see definition of crashreporter_annotations_t in
+ // mach_vm_compat.h).
+ mach_vm_address_t GetCrashInfo() const {
+ return reinterpret_cast<mach_vm_address_t>(&crash_info_[0]);
+ }
+
+ // Size of the locally cached __DATA,__crash_info section. This will be zero
+ // if the vector is empty. But even if it's non-zero, the __crash_info
+ // section of which it's a copy may be empty of useful data.
+ size_t GetCrashInfoSize() const {return crash_info_.size();}
+
+ // Task owning this loaded image
+ mach_port_t GetTask() {return task_;}
+
+ // CPU type of the task and the image
+ cpu_type_t GetCPUType() {return cpu_type_;}
+
+ // CPU subtype of the image
+ cpu_type_t GetCPUSubtype() {return cpu_subtype_;}
+
+ // filetype from the Mach-O header.
+ uint32_t GetFileType();
+
+ // Return true if the task is a 64-bit architecture.
+ bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
+
+ uint32_t GetVersion() {return version_;}
+ // For sorting
+ bool operator<(const DynamicImage &inInfo) {
+ return GetLoadAddress() < inInfo.GetLoadAddress();
+ }
+
+ // Sanity checking
+ bool IsValid() {return GetVMSize() != 0;}
+
+ private:
+ DynamicImage(const DynamicImage &);
+ DynamicImage &operator=(const DynamicImage &);
+
+ friend class DynamicImages;
+ template<typename MachBits>
+ friend bool FindTextSection(DynamicImage& image);
+ template<typename MachBits>
+ friend uint32_t GetFileTypeFromHeader(DynamicImage& image);
+
+ // Initializes vmaddr_, vmsize_, and slide_
+ void CalculateMemoryAndVersionInfo();
+
+ const vector<uint8_t> header_; // our local copy of the header
+ size_t header_size_; // mach_header plus load commands
+ uint64_t load_address_; // base address image is mapped into
+ mach_vm_address_t vmaddr_;
+ mach_vm_size_t vmsize_;
+ ptrdiff_t slide_;
+ vector<uint8_t> crash_info_;
+ uint32_t version_; // Dylib version
+ string file_path_; // path dyld used to load the image
+ uintptr_t file_mod_date_; // time_t of image file
+ bool is_dyld_; // Is image file dyld itself?
+
+ mach_port_t task_;
+ cpu_type_t cpu_type_; // CPU type of task_ and image
+ cpu_subtype_t cpu_subtype_; // CPU subtype of image
+ ptrdiff_t shared_cache_slide_; // Task's shared cache slide
+};
+
+//==============================================================================
+// DynamicImageRef is just a simple wrapper for a pointer to
+// DynamicImage. The reason we use it instead of a simple typedef is so
+// that we can use stl::sort() on a vector of DynamicImageRefs
+// and simple class pointers can't implement operator<().
+//
+class DynamicImageRef {
+ public:
+ explicit DynamicImageRef(DynamicImage *inP) : p(inP) {}
+ // The copy constructor is required by STL
+ DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {}
+
+ bool operator<(const DynamicImageRef &inRef) const {
+ return (*const_cast<DynamicImageRef*>(this)->p)
+ < (*const_cast<DynamicImageRef&>(inRef).p);
+ }
+
+ bool operator==(const DynamicImageRef &inInfo) const {
+ return (*const_cast<DynamicImageRef*>(this)->p).GetLoadAddress() ==
+ (*const_cast<DynamicImageRef&>(inInfo)).GetLoadAddress();
+ }
+
+ // Be just like DynamicImage*
+ DynamicImage *operator->() {return p;}
+ operator DynamicImage*() {return p;}
+
+ private:
+ DynamicImage *p;
+};
+
+// Helper function to deal with 32-bit/64-bit Mach-O differences.
+class DynamicImages;
+template<typename MachBits>
+void ReadOneImageInfo(DynamicImages& images, uint64_t image_address,
+ uint64_t file_path_address, uint64_t file_mod_date,
+ uint64_t shared_cache_slide, bool is_dyld);
+template<typename MachBits>
+void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
+
+//==============================================================================
+// An object of type DynamicImages may be created to allow introspection of
+// an arbitrary task's dynamically loaded mach-o binaries. This makes the
+// assumption that the current task has send rights to the target task.
+class DynamicImages {
+ public:
+ explicit DynamicImages(mach_port_t task);
+
+ ~DynamicImages() {
+ for (int i = 0; i < GetImageCount(); ++i) {
+ delete image_list_[i];
+ }
+ }
+
+ // Returns the number of dynamically loaded mach-o images.
+ int GetImageCount() const {return static_cast<int>(image_list_.size());}
+
+ // Returns an individual image.
+ DynamicImage *GetImage(int i) {
+ if (i < (int)image_list_.size()) {
+ return image_list_[i];
+ }
+ return NULL;
+ }
+
+ // Returns the image corresponding to the main executable.
+ DynamicImage *GetExecutableImage();
+ int GetExecutableImageIndex();
+
+ // Returns the task which we're looking at.
+ mach_port_t GetTask() const {return task_;}
+
+ // CPU type of the task
+ cpu_type_t GetCPUType() {return cpu_type_;}
+
+ // Return true if the task is a 64-bit architecture.
+ bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
+
+ // Determine the CPU type of the task being dumped.
+ static cpu_type_t DetermineTaskCPUType(task_t task);
+
+ // Get the native CPU type of this task.
+ static cpu_type_t GetNativeCPUType() {
+#if defined(__i386__)
+ return CPU_TYPE_I386;
+#elif defined(__x86_64__)
+ return CPU_TYPE_X86_64;
+#elif defined(__ppc__)
+ return CPU_TYPE_POWERPC;
+#elif defined(__ppc64__)
+ return CPU_TYPE_POWERPC64;
+#elif defined(__arm__)
+ return CPU_TYPE_ARM;
+#elif defined(__aarch64__)
+ return CPU_TYPE_ARM64;
+#else
+#error "GetNativeCPUType not implemented for this architecture"
+#endif
+ }
+
+ private:
+ template<typename MachBits>
+ friend void ReadOneImageInfo(DynamicImages& images, uint64_t image_address,
+ uint64_t file_path_address, uint64_t file_mod_date,
+ uint64_t shared_cache_slide, bool is_dyld);
+ template<typename MachBits>
+ friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
+
+ bool IsOurTask() {return task_ == mach_task_self();}
+
+ // Initialization
+ void ReadImageInfoForTask();
+ uint64_t GetDyldAllImageInfosPointer();
+
+ mach_port_t task_;
+ cpu_type_t cpu_type_; // CPU type of task_
+ vector<DynamicImageRef> image_list_;
+};
+
+// Fill bytes with the contents of memory at a particular
+// location in another task.
+kern_return_t ReadTaskMemory(task_port_t target_task,
+ const uint64_t address,
+ size_t length,
+ vector<uint8_t> &bytes);
+
+std::string ReadTaskString(task_port_t target_task,
+ const uint64_t address);
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc
new file mode 100644
index 0000000000..75dd722fee
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc
@@ -0,0 +1,991 @@
+// 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 <mach/exc.h>
+#include <mach/mig.h>
+#include <pthread.h>
+#include <signal.h>
+#include <TargetConditionals.h>
+
+#include <map>
+
+#include "mac/handler/exception_handler.h"
+#include "mac/handler/minidump_generator.h"
+#include "common/mac/macho_utilities.h"
+#include "common/mac/scoped_task_suspend-inl.h"
+#include "google_breakpad/common/minidump_exception_mac.h"
+#include "mozilla/Assertions.h"
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#endif
+
+#ifndef __EXCEPTIONS
+// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
+// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
+// exceptions disabled even when other C++ libraries are used. #undef the try
+// and catch macros first in case libstdc++ is in use and has already provided
+// its own definitions.
+#undef try
+#define try if (true)
+#undef catch
+#define catch(X) if (false)
+#endif // __EXCEPTIONS
+
+#ifndef USE_PROTECTED_ALLOCATIONS
+#if TARGET_OS_IPHONE
+#define USE_PROTECTED_ALLOCATIONS 1
+#else
+#define USE_PROTECTED_ALLOCATIONS 0
+#endif
+#endif
+
+// If USE_PROTECTED_ALLOCATIONS is activated then the
+// gBreakpadAllocator needs to be setup in other code
+// ahead of time. Please see ProtectedMemoryAllocator.h
+// for more details.
+#if USE_PROTECTED_ALLOCATIONS
+ #include "protected_memory_allocator.h"
+ extern ProtectedMemoryAllocator *gBreakpadAllocator;
+#endif
+
+namespace google_breakpad {
+
+static union {
+#if USE_PROTECTED_ALLOCATIONS
+#if defined PAGE_MAX_SIZE
+ char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE)));
+#else
+ char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+#endif // defined PAGE_MAX_SIZE
+#endif // USE_PROTECTED_ALLOCATIONS
+ google_breakpad::ExceptionHandler *handler;
+} gProtectedData;
+
+using std::map;
+
+// These structures and techniques are illustrated in
+// Mac OS X Internals, Amit Singh, ch 9.7
+#pragma pack(push, 4)
+struct ExceptionMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
+ char padding[512];
+};
+#pragma pack(pop)
+
+struct ExceptionParameters {
+ ExceptionParameters() : count(0) {}
+ mach_msg_type_number_t count;
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+};
+
+#pragma pack(push, 4)
+struct ExceptionReplyMessage {
+ mach_msg_header_t header;
+ NDR_record_t ndr;
+ kern_return_t return_code;
+};
+#pragma pack(pop)
+
+// Only catch these three exceptions. The other ones are nebulously defined
+// and may result in treating a non-fatal exception as fatal.
+exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
+EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT |
+EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD;
+
+kern_return_t ForwardException(mach_port_t task,
+ mach_port_t failed_thread,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t code_count);
+
+// The contents of mach_exc_server() and mach_exception_raise() are derived
+// from /usr/include/mach/mach_exc.defs, as follows:
+//
+// 1) Run 'mig mach_exc.defs' which creates the following files:
+// mach_exc.h
+// mach_excServer.c
+// mach_excUser.c
+// 2) The relevant code for mach_exc_server() comes from the following methods
+// in mach_excServer.c:
+// mach_exc_server()
+// _Xmach_exception_raise()
+// 3) The relevant code for mach_exception_raise() comes from the following
+// method in mach_excUser.c:
+// mach_exception_raise()
+boolean_t mach_exc_server(mach_msg_header_t* InHeadP,
+ mach_msg_header_t* OutHeadP)
+{
+ OutHeadP->msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
+ OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
+ /* Minimal size: mach_exception_raise() will update it if different */
+ OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
+ OutHeadP->msgh_local_port = MACH_PORT_NULL;
+ OutHeadP->msgh_id = InHeadP->msgh_id + 100;
+ OutHeadP->msgh_reserved = 0;
+
+ if (InHeadP->msgh_id != 2405) {
+ ((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
+ ((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
+ return FALSE;
+ }
+
+#pragma pack(push, 4)
+ typedef struct {
+ mach_msg_header_t Head;
+ /* start of the kernel processed data */
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ /* end of the kernel processed data */
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ mach_msg_trailer_t trailer;
+ } Request;
+
+ typedef struct {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ } Reply;
+#pragma pack(pop)
+
+ Request* In0P = (Request*)InHeadP;
+ Reply* OutP = (Reply*)OutHeadP;
+
+ if (In0P->task.name != mach_task_self()) {
+ // This exception was not meant for us, we avoid forwarding it (because it
+ // could cause a loop in the exception handler) and simply ignore it
+ // instead.
+ return TRUE;
+ }
+
+ OutP->RetCode = ForwardException(In0P->task.name,
+ In0P->thread.name,
+ In0P->exception,
+ In0P->code,
+ In0P->codeCnt);
+ OutP->NDR = NDR_record;
+ return TRUE;
+}
+
+kern_return_t mach_exception_raise(mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt)
+{
+#pragma pack(push, 4)
+ typedef struct {
+ mach_msg_header_t Head;
+ /* start of the kernel processed data */
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ /* end of the kernel processed data */
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ } Request;
+
+ typedef struct {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ mach_msg_trailer_t trailer;
+ } Reply;
+#pragma pack(pop)
+
+ Request In;
+ Request *InP = &In;
+
+ mach_msg_return_t msg_result;
+ unsigned int msgh_size;
+
+ InP->msgh_body.msgh_descriptor_count = 2;
+ InP->thread.name = thread;
+ InP->thread.disposition = 19;
+ InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
+ InP->task.name = task;
+ InP->task.disposition = 19;
+ InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
+
+ InP->NDR = NDR_record;
+
+ InP->exception = exception;
+
+ if (codeCnt > 2) {
+ { return MIG_ARRAY_TOO_LARGE; }
+ }
+ (void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
+
+ InP->codeCnt = codeCnt;
+
+ msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
+ InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ /* msgh_size passed as argument */
+ InP->Head.msgh_remote_port = exception_port;
+ InP->Head.msgh_local_port = mig_get_reply_port();
+ InP->Head.msgh_id = 2405;
+ InP->Head.msgh_reserved = 0;
+
+ msg_result = mach_msg(&InP->Head,
+ MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE,
+ msgh_size,
+ (mach_msg_size_t)sizeof(Reply),
+ InP->Head.msgh_local_port,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ if (msg_result != MACH_MSG_SUCCESS) {
+ fprintf(stderr, "** mach_msg() error forwarding exception!!\n");
+ return msg_result;
+ }
+
+ return KERN_SUCCESS;
+}
+
+ExceptionHandler::ExceptionHandler(const string &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ bool install_handler,
+ const char* port_name)
+ : dump_path_(),
+ filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ directCallback_(NULL),
+ handler_thread_(NULL),
+ handler_port_(MACH_PORT_NULL),
+ previous_(NULL),
+ installed_exception_handler_(false),
+ is_in_teardown_(false),
+ last_minidump_write_result_(false),
+ use_minidump_write_mutex_(false) {
+ // This will update to the ID and C-string pointers
+ set_dump_path(dump_path);
+ MinidumpGenerator::GatherSystemInformation();
+#if !TARGET_OS_IPHONE
+ if (port_name)
+ crash_generation_client_.reset(new CrashGenerationClient(port_name));
+#endif
+ Setup(install_handler);
+}
+
+// special constructor if we want to bypass minidump writing and
+// simply get a callback with the exception information
+ExceptionHandler::ExceptionHandler(DirectCallback callback,
+ void* callback_context,
+ bool install_handler)
+ : dump_path_(),
+ filter_(NULL),
+ callback_(NULL),
+ callback_context_(callback_context),
+ directCallback_(callback),
+ handler_thread_(NULL),
+ handler_port_(MACH_PORT_NULL),
+ previous_(NULL),
+ installed_exception_handler_(false),
+ is_in_teardown_(false),
+ last_minidump_write_result_(false),
+ use_minidump_write_mutex_(false) {
+ MinidumpGenerator::GatherSystemInformation();
+ Setup(install_handler);
+}
+
+ExceptionHandler::~ExceptionHandler() {
+ Teardown();
+}
+
+bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
+ // If we're currently writing, just return
+ if (use_minidump_write_mutex_)
+ return false;
+
+ use_minidump_write_mutex_ = true;
+ last_minidump_write_result_ = false;
+
+ // Lock the mutex. Since we just created it, this will return immediately.
+ if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
+ // Send an empty message to the handle port so that a minidump will
+ // be written
+ bool result = SendMessageToHandlerThread(write_exception_stream ?
+ kWriteDumpWithExceptionMessage :
+ kWriteDumpMessage);
+ if (!result) {
+ pthread_mutex_unlock(&minidump_write_mutex_);
+ return false;
+ }
+
+ // Wait for the minidump writer to complete its writing. It will unlock
+ // the mutex when completed
+ pthread_mutex_lock(&minidump_write_mutex_);
+ }
+
+ use_minidump_write_mutex_ = false;
+ UpdateNextID();
+ return last_minidump_write_result_;
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const string &dump_path,
+ bool write_exception_stream,
+ MinidumpCallback callback,
+ void* callback_context) {
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
+ NULL);
+ return handler.WriteMinidump(write_exception_stream);
+}
+
+// static
+bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
+ mach_port_t child_blamed_thread,
+ const string &dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
+ ScopedTaskSuspend suspend(child);
+
+ MinidumpGenerator generator(child, MACH_PORT_NULL);
+ string dump_id;
+ string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
+
+ generator.SetExceptionInformation(EXC_BREAKPOINT,
+#if defined(__i386__) || defined(__x86_64__)
+ EXC_I386_BPT,
+#elif defined(__ppc__) || defined(__ppc64__)
+ EXC_PPC_BREAKPOINT,
+#elif defined(__arm__) || defined(__aarch64__)
+ EXC_ARM_BREAKPOINT,
+#else
+#error architecture not supported
+#endif
+ 0,
+ child_blamed_thread);
+ bool result = generator.Write(dump_filename.c_str());
+
+ if (callback) {
+ return callback(dump_path.c_str(), dump_id.c_str(),
+ callback_context, nullptr, result);
+ }
+ return result;
+}
+
+#ifdef MOZ_PHC
+static void GetPHCAddrInfo(int exception_type, int64_t exception_subcode,
+ mozilla::phc::AddrInfo* addr_info) {
+ // Is this a crash involving a PHC allocation?
+ if (exception_type == EXC_BAD_ACCESS) {
+ // `exception_subcode` is only non-zero when it's a bad access, in which
+ // case it holds the address of the bad access.
+ char* addr = reinterpret_cast<char*>(exception_subcode);
+ mozilla::phc::IsPHCAllocation(addr, addr_info);
+ }
+}
+#endif
+
+bool ExceptionHandler::WriteMinidumpWithException(
+ int exception_type,
+ int64_t exception_code,
+ int64_t exception_subcode,
+ breakpad_ucontext_t* task_context,
+ mach_port_t thread_name,
+ mach_port_t task_name,
+ bool exit_after_write,
+ bool report_current_thread) {
+ bool result = false;
+
+#if TARGET_OS_IPHONE
+ // _exit() should never be called on iOS.
+ exit_after_write = false;
+#endif
+
+ mozilla::phc::AddrInfo* addr_info = nullptr;
+#ifdef MOZ_PHC
+ addr_info = &mozilla::phc::gAddrInfo;
+ GetPHCAddrInfo(exception_type, exception_subcode, addr_info);
+#endif
+
+ if (directCallback_) {
+ if (directCallback_(callback_context_,
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name) ) {
+ if (exit_after_write)
+ _exit(exception_type);
+ }
+#if !TARGET_OS_IPHONE
+ } else if (IsOutOfProcess()) {
+ if (exception_type && exception_code) {
+ // If this is a real exception, give the filter (if any) a chance to
+ // decide if this should be sent.
+ if (filter_ && !filter_(callback_context_))
+ return false;
+ result = crash_generation_client_->RequestDumpForException(
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name,
+ task_name);
+
+ if (callback_) {
+ result = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
+ addr_info, result);
+ }
+
+ if (result && exit_after_write) {
+ _exit(exception_type);
+ }
+ }
+#endif
+ } else {
+ string minidump_id;
+
+ // Putting the MinidumpGenerator in its own context will ensure that the
+ // destructor is executed, closing the newly created minidump file.
+ if (!dump_path_.empty()) {
+ MinidumpGenerator md(mach_task_self(),
+ report_current_thread ? MACH_PORT_NULL :
+ mach_thread_self());
+ md.SetTaskContext(task_context);
+ if (exception_type && exception_code) {
+ // If this is a real exception, give the filter (if any) a chance to
+ // decide if this should be sent.
+ if (filter_ && !filter_(callback_context_))
+ return false;
+
+ md.SetExceptionInformation(exception_type, exception_code,
+ exception_subcode, thread_name);
+ }
+
+ result = md.Write(next_minidump_path_c_);
+ }
+
+ // Call user specified callback (if any)
+ if (callback_) {
+ result = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
+ addr_info, result);
+ // If the user callback returned true and we're handling an exception
+ // (rather than just writing out the file), then we should exit without
+ // forwarding the exception to the next handler.
+ if (result) {
+ if (exit_after_write)
+ _exit(exception_type);
+ }
+ }
+ }
+
+ return result;
+}
+
+kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t code_count) {
+ // At this time, we should have called Uninstall() on the exception handler
+ // so that the current exception ports are the ones that we should be
+ // forwarding to.
+ ExceptionParameters current;
+
+ current.count = EXC_TYPES_COUNT;
+ mach_port_t current_task = mach_task_self();
+ task_get_exception_ports(current_task,
+ s_exception_mask,
+ current.masks,
+ &current.count,
+ current.ports,
+ current.behaviors,
+ current.flavors);
+
+ // Find the first exception handler that matches the exception
+ unsigned int found;
+ for (found = 0; found < current.count; ++found) {
+ if (current.masks[found] & (1 << exception)) {
+ break;
+ }
+ }
+
+ // Nothing to forward
+ if (found == current.count) {
+ fprintf(stderr, "** No previous ports for forwarding!! \n");
+ _exit(KERN_FAILURE);
+ }
+
+ mach_port_t target_port = current.ports[found];
+ exception_behavior_t target_behavior = current.behaviors[found];
+
+ kern_return_t result;
+ switch (target_behavior & ~MACH_EXCEPTION_CODES) {
+ case EXCEPTION_DEFAULT:
+ result = mach_exception_raise(target_port, failed_thread, task, exception,
+ code, code_count);
+ break;
+ default:
+ fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
+ result = KERN_FAILURE;
+ break;
+ }
+
+ return result;
+}
+
+// static
+void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
+ pthread_setname_np("Breakpad ExceptionHandler");
+
+ ExceptionHandler* self =
+ reinterpret_cast<ExceptionHandler*>(exception_handler_class);
+ ExceptionMessage receive;
+
+ // Wait for the exception info
+ while (1) {
+ receive.header.msgh_local_port = self->handler_port_;
+ receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
+ kern_return_t result = mach_msg(&(receive.header),
+ MACH_RCV_MSG | MACH_RCV_LARGE, 0,
+ receive.header.msgh_size,
+ self->handler_port_,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+
+ if (result == KERN_SUCCESS) {
+ // Uninstall our handler so that we don't get in a loop if the process of
+ // writing out a minidump causes an exception. However, if the exception
+ // was caused by a fork'd process, don't uninstall things
+
+ // If the actual exception code is zero, then we're calling this handler
+ // in a way that indicates that we want to either exit this thread or
+ // generate a minidump
+ //
+ // While reporting, all threads (except this one) must be suspended
+ // to avoid misleading stacks. If appropriate they will be resumed
+ // afterwards.
+ if (!receive.exception) {
+ // Don't touch self, since this message could have been sent
+ // from its destructor.
+ if (receive.header.msgh_id == kShutdownMessage)
+ return NULL;
+
+ self->SuspendThreads();
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ mach_port_t thread = MACH_PORT_NULL;
+ int exception_type = 0;
+ int64_t exception_code = 0;
+ if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
+ thread = receive.thread.name;
+ exception_type = EXC_BREAKPOINT;
+#if defined(__i386__) || defined(__x86_64__)
+ exception_code = EXC_I386_BPT;
+#elif defined(__ppc__) || defined(__ppc64__)
+ exception_code = EXC_PPC_BREAKPOINT;
+#elif defined(__arm__) || defined(__aarch64__)
+ exception_code = EXC_ARM_BREAKPOINT;
+#else
+#error architecture not supported
+#endif
+ }
+
+ // Write out the dump and save the result for later retrieval
+ self->last_minidump_write_result_ =
+ self->WriteMinidumpWithException(exception_type, exception_code,
+ 0, NULL, thread, mach_task_self(),
+ false, false);
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+
+ self->ResumeThreads();
+
+ if (self->use_minidump_write_mutex_)
+ pthread_mutex_unlock(&self->minidump_write_mutex_);
+ } else {
+ bool crash_reported = false;
+
+ // When forking a child process with the exception handler installed,
+ // if the child crashes, it will send the exception back to the parent
+ // process. The check for task == self_task() ensures that only
+ // exceptions that occur in the parent process are caught and
+ // processed.
+ if (receive.task.name == mach_task_self()) {
+ self->SuspendThreads();
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ mach_exception_data_type_t subcode = 0;
+ if (receive.code_count > 1) {
+ switch (receive.exception) {
+ case EXC_BAD_ACCESS:
+ case EXC_CRASH:
+ case EXC_RESOURCE:
+ case EXC_GUARD:
+ subcode = receive.code[1];
+ break;
+ default:
+ subcode = 0;
+ }
+ }
+
+ // Generate the minidump with the exception data.
+ crash_reported =
+ self->WriteMinidumpWithException(receive.exception, receive.code[0],
+ subcode, NULL, receive.thread.name,
+ mach_task_self(), true, false);
+
+#if USE_PROTECTED_ALLOCATIONS
+ // This may have become protected again within
+ // WriteMinidumpWithException, but it needs to be unprotected for
+ // UninstallHandler.
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ self->UninstallHandler(true);
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+ }
+
+ ExceptionReplyMessage reply;
+ if (!mach_exc_server(&receive.header, &reply.header)) {
+ MOZ_CRASH_UNSAFE_PRINTF("Mach message id: %d crash reported = %d",
+ receive.header.msgh_id, crash_reported);
+ }
+
+ // Send a reply and exit
+ mach_msg(&(reply.header), MACH_SEND_MSG,
+ reply.header.msgh_size, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// static
+void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+ gProtectedData.handler->WriteMinidumpWithException(
+ EXC_SOFTWARE,
+ MD_EXCEPTION_CODE_MAC_ABORT,
+ 0,
+ static_cast<breakpad_ucontext_t*>(uc),
+ mach_thread_self(),
+ mach_task_self(),
+ true,
+ true);
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+}
+
+// static
+bool ExceptionHandler::WriteForwardedExceptionMinidump(int exception_type,
+ int64_t exception_code,
+ int64_t exception_subcode,
+ mach_port_t thread,
+ mach_port_t task)
+{
+ if (!gProtectedData.handler) {
+ return false;
+ }
+ return gProtectedData.handler->WriteMinidumpWithException(exception_type, exception_code,
+ exception_subcode, NULL, thread, task,
+ /* exit_after_write = */ false,
+ /* report_current_thread = */ true);
+}
+
+bool ExceptionHandler::InstallHandler() {
+ // If a handler is already installed, something is really wrong.
+ if (gProtectedData.handler != NULL) {
+ return false;
+ }
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGABRT);
+ sa.sa_sigaction = ExceptionHandler::SignalHandler;
+ sa.sa_flags = SA_SIGINFO;
+
+ scoped_ptr<struct sigaction> old(new struct sigaction);
+ if (sigaction(SIGABRT, &sa, old.get()) == -1) {
+ return false;
+ }
+ old_handler_.swap(old);
+ gProtectedData.handler = this;
+#if USE_PROTECTED_ALLOCATIONS
+ assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
+ mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
+#endif
+
+ try {
+#if USE_PROTECTED_ALLOCATIONS
+ previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
+ ExceptionParameters();
+#else
+ previous_ = new ExceptionParameters();
+#endif
+ }
+ catch (std::bad_alloc) {
+ return false;
+ }
+
+ // Save the current exception ports so that we can forward to them
+ previous_->count = EXC_TYPES_COUNT;
+ mach_port_t current_task = mach_task_self();
+ kern_return_t result = task_get_exception_ports(current_task,
+ s_exception_mask,
+ previous_->masks,
+ &previous_->count,
+ previous_->ports,
+ previous_->behaviors,
+ previous_->flavors);
+
+ // Setup the exception ports on this task. Such documentation as exists for
+ // task_set_exception_port() and friends can be found in the source code for
+ // xnu. Apple's implementation is available at https://opensource.apple.com/.
+ if (result == KERN_SUCCESS)
+ result = task_set_exception_ports(current_task, s_exception_mask,
+ handler_port_,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+ THREAD_STATE_NONE);
+
+ installed_exception_handler_ = (result == KERN_SUCCESS);
+
+ return installed_exception_handler_;
+}
+
+bool ExceptionHandler::UninstallHandler(bool in_exception) {
+ kern_return_t result = KERN_SUCCESS;
+
+ if (old_handler_.get()) {
+ sigaction(SIGABRT, old_handler_.get(), NULL);
+#if USE_PROTECTED_ALLOCATIONS
+ mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
+ PROT_READ | PROT_WRITE);
+#endif
+ // If we're handling an exception, leak the sigaction struct
+ // because it is unsafe to delete objects while in exception
+ // handling context.
+ if (in_exception) {
+ old_handler_.release();
+ } else {
+ old_handler_.reset();
+ }
+ gProtectedData.handler = NULL;
+ }
+
+ if (installed_exception_handler_) {
+ mach_port_t current_task = mach_task_self();
+
+ // Restore the previous ports
+ for (unsigned int i = 0; i < previous_->count; ++i) {
+ result = task_set_exception_ports(current_task, previous_->masks[i],
+ previous_->ports[i],
+ previous_->behaviors[i],
+ previous_->flavors[i]);
+ if (result != KERN_SUCCESS)
+ return false;
+ }
+
+ // this delete should NOT happen if an exception just occurred!
+ if (!in_exception) {
+#if USE_PROTECTED_ALLOCATIONS
+ previous_->~ExceptionParameters();
+#else
+ delete previous_;
+#endif
+ }
+
+ previous_ = NULL;
+ installed_exception_handler_ = false;
+ }
+
+ return result == KERN_SUCCESS;
+}
+
+bool ExceptionHandler::Setup(bool install_handler) {
+ if (pthread_mutex_init(&minidump_write_mutex_, NULL))
+ return false;
+
+ // Create a receive right
+ mach_port_t current_task = mach_task_self();
+ kern_return_t result = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &handler_port_);
+ // Add send right
+ if (result == KERN_SUCCESS)
+ result = mach_port_insert_right(current_task, handler_port_, handler_port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (install_handler && result == KERN_SUCCESS)
+ if (!InstallHandler())
+ return false;
+
+ if (result == KERN_SUCCESS) {
+ // Install the handler in its own thread, detached as we won't be joining.
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ int thread_create_result = pthread_create(&handler_thread_, &attr,
+ &WaitForMessage, this);
+ pthread_attr_destroy(&attr);
+ result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
+ }
+
+ return result == KERN_SUCCESS;
+}
+
+bool ExceptionHandler::Teardown() {
+ kern_return_t result = KERN_SUCCESS;
+ is_in_teardown_ = true;
+
+ if (!UninstallHandler(false))
+ return false;
+
+ // Send an empty message so that the handler_thread exits
+ if (SendMessageToHandlerThread(kShutdownMessage)) {
+ mach_port_t current_task = mach_task_self();
+ result = mach_port_deallocate(current_task, handler_port_);
+ if (result != KERN_SUCCESS)
+ return false;
+ } else {
+ return false;
+ }
+
+ handler_thread_ = NULL;
+ handler_port_ = MACH_PORT_NULL;
+ pthread_mutex_destroy(&minidump_write_mutex_);
+
+ return result == KERN_SUCCESS;
+}
+
+bool ExceptionHandler::SendMessageToHandlerThread(
+ HandlerThreadMessage message_id) {
+ ExceptionMessage msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.header.msgh_id = message_id;
+ if (message_id == kWriteDumpMessage ||
+ message_id == kWriteDumpWithExceptionMessage) {
+ // Include this thread's port.
+ msg.thread.name = mach_thread_self();
+ msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
+ msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+ msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
+ msg.header.msgh_remote_port = handler_port_;
+ msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ kern_return_t result = mach_msg(&(msg.header),
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ msg.header.msgh_size, 0, 0,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ return result == KERN_SUCCESS;
+}
+
+void ExceptionHandler::UpdateNextID() {
+ next_minidump_path_ =
+ (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
+
+ next_minidump_path_c_ = next_minidump_path_.c_str();
+ next_minidump_id_c_ = next_minidump_id_.c_str();
+}
+
+bool ExceptionHandler::SuspendThreads() {
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
+ return false;
+
+ // suspend all of the threads except for this one
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ if (threads_for_task[i] != mach_thread_self()) {
+ if (thread_suspend(threads_for_task[i]))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ExceptionHandler::ResumeThreads() {
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
+ return false;
+
+ // resume all of the threads except for this one
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ if (threads_for_task[i] != mach_thread_self()) {
+ if (thread_resume(threads_for_task[i]))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h
new file mode 100644
index 0000000000..24728a8c7c
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h
@@ -0,0 +1,297 @@
+// 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.
+
+// exception_handler.h: MacOS exception handler
+// This class can install a Mach exception port handler to trap most common
+// programming errors. If an exception occurs, a minidump file will be
+// generated which contains detailed information about the process and the
+// exception.
+
+#ifndef CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
+#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
+
+#include <mach/mach.h>
+#include <TargetConditionals.h>
+
+#include <string>
+
+#include "mac/handler/ucontext_compat.h"
+#include "common/scoped_ptr.h"
+
+#if !TARGET_OS_IPHONE
+#include "mac/crash_generation/crash_generation_client.h"
+#endif
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#else
+namespace mozilla { namespace phc { class AddrInfo {}; } }
+#endif
+
+namespace google_breakpad {
+
+using std::string;
+
+struct ExceptionParameters;
+
+enum HandlerThreadMessage {
+ // Message ID telling the handler thread to write a dump.
+ kWriteDumpMessage = 0,
+ // Message ID telling the handler thread to write a dump and include
+ // an exception stream.
+ kWriteDumpWithExceptionMessage = 1,
+ // Message ID telling the handler thread to quit.
+ kShutdownMessage = 2
+};
+
+class ExceptionHandler {
+ public:
+ // A callback function to run before Breakpad performs any substantial
+ // processing of an exception. A FilterCallback is called before writing
+ // a minidump. context is the parameter supplied by the user as
+ // callback_context when the handler was created.
+ //
+ // If a FilterCallback returns true, Breakpad will continue processing,
+ // attempting to write a minidump. If a FilterCallback returns false, Breakpad
+ // will immediately report the exception as unhandled without writing a
+ // minidump, allowing another handler the opportunity to handle it.
+ typedef bool (*FilterCallback)(void *context);
+
+ // A callback function to run after the minidump has been written.
+ // |minidump_id| is a unique id for the dump, so the minidump
+ // file is <dump_dir>/<minidump_id>.dmp.
+ // |context| is the value passed into the constructor.
+ // |succeeded| indicates whether a minidump file was successfully written.
+ // Return true if the exception was fully handled and breakpad should exit.
+ // Return false to allow any other exception handlers to process the
+ // exception.
+ typedef bool (*MinidumpCallback)(const char *dump_dir,
+ const char *minidump_id,
+ void *context,
+ const mozilla::phc::AddrInfo* addr_info,
+ bool succeeded);
+
+ // A callback function which will be called directly if an exception occurs.
+ // This bypasses the minidump file writing and simply gives the client
+ // the exception information.
+ typedef bool (*DirectCallback)( void *context,
+ int exception_type,
+ int exception_code,
+ int64_t exception_subcode,
+ mach_port_t thread_name);
+
+ // Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Minidump files will be written to dump_path, and the optional callback
+ // is called after writing the dump file, as described above.
+ // If install_handler is true, then a minidump will be written whenever
+ // an unhandled exception occurs. If it is false, minidumps will only
+ // be written when WriteMinidump is called.
+ // If port_name is non-NULL, attempt to perform out-of-process dump generation
+ // If port_name is NULL, in-process dump generation will be used.
+ ExceptionHandler(const string &dump_path,
+ FilterCallback filter, MinidumpCallback callback,
+ void *callback_context, bool install_handler,
+ const char *port_name);
+
+ // A special constructor if we want to bypass minidump writing and
+ // simply get a callback with the exception information.
+ ExceptionHandler(DirectCallback callback,
+ void *callback_context,
+ bool install_handler);
+
+ ~ExceptionHandler();
+
+ // Get and set the minidump path.
+ string dump_path() const { return dump_path_; }
+ void set_dump_path(const string &dump_path) {
+ dump_path_ = dump_path;
+ dump_path_c_ = dump_path_.c_str();
+ UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_.
+ }
+
+ // Writes a minidump immediately. This can be used to capture the
+ // execution state independently of a crash. Returns true on success.
+ bool WriteMinidump() {
+ return WriteMinidump(false);
+ }
+
+ bool WriteMinidump(bool write_exception_stream);
+
+ // Convenience form of WriteMinidump which does not require an
+ // ExceptionHandler instance.
+ static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
+ void *callback_context) {
+ return WriteMinidump(dump_path, false, callback, callback_context);
+ }
+
+ static bool WriteMinidump(const string &dump_path,
+ bool write_exception_stream,
+ MinidumpCallback callback,
+ void *callback_context);
+
+ // Write a minidump of child immediately. This can be used to capture
+ // the execution state of a child process independently of a crash.
+ static bool WriteMinidumpForChild(mach_port_t child,
+ mach_port_t child_blamed_thread,
+ const std::string &dump_path,
+ MinidumpCallback callback,
+ void *callback_context);
+
+ // Write a minidump for an exception that was received by another handler.
+ static bool WriteForwardedExceptionMinidump(int exception_type,
+ int64_t exception_code,
+ int64_t exception_subcode,
+ mach_port_t thread,
+ mach_port_t task);
+
+ // Returns whether out-of-process dump generation is used or not.
+ bool IsOutOfProcess() const {
+#if TARGET_OS_IPHONE
+ return false;
+#else
+ return crash_generation_client_.get() != NULL;
+#endif
+ }
+
+ private:
+ // Install the mach exception handler
+ bool InstallHandler();
+
+ // Uninstall the mach exception handler (if any)
+ bool UninstallHandler(bool in_exception);
+
+ // Setup the handler thread, and if |install_handler| is true, install the
+ // mach exception port handler
+ bool Setup(bool install_handler);
+
+ // Uninstall the mach exception handler (if any) and terminate the helper
+ // thread
+ bool Teardown();
+
+ // Send a mach message to the exception handler. Return true on
+ // success, false otherwise.
+ bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
+
+ // All minidump writing goes through this one routine.
+ // |task_context| can be NULL. If not, it will be used to retrieve the
+ // context of the current thread, instead of using |thread_get_state|.
+ bool WriteMinidumpWithException(int exception_type,
+ int64_t exception_code,
+ int64_t exception_subcode,
+ breakpad_ucontext_t *task_context,
+ mach_port_t thread_name,
+ mach_port_t task_name,
+ bool exit_after_write,
+ bool report_current_thread);
+
+ // When installed, this static function will be call from a newly created
+ // pthread with |this| as the argument
+ static void *WaitForMessage(void *exception_handler_class);
+
+ // Signal handler for SIGABRT.
+ static void SignalHandler(int sig, siginfo_t* info, void* uc);
+
+ // disallow copy ctor and operator=
+ explicit ExceptionHandler(const ExceptionHandler &);
+ void operator=(const ExceptionHandler &);
+
+ // Generates a new ID and stores it in next_minidump_id_, and stores the
+ // path of the next minidump to be written in next_minidump_path_.
+ void UpdateNextID();
+
+ // These functions will suspend/resume all threads except for the
+ // reporting thread
+ bool SuspendThreads();
+ bool ResumeThreads();
+
+ // The destination directory for the minidump
+ string dump_path_;
+
+ // The basename of the next minidump w/o extension
+ string next_minidump_id_;
+
+ // The full path to the next minidump to be written, including extension
+ string next_minidump_path_;
+
+ // Pointers to the UTF-8 versions of above
+ const char *dump_path_c_;
+ const char *next_minidump_id_c_;
+ const char *next_minidump_path_c_;
+
+ // The callback function and pointer to be passed back after the minidump
+ // has been written
+ FilterCallback filter_;
+ MinidumpCallback callback_;
+ void *callback_context_;
+
+ // The callback function to be passed back when we don't want a minidump
+ // file to be written
+ DirectCallback directCallback_;
+
+ // The thread that is created for the handler
+ pthread_t handler_thread_;
+
+ // The port that is waiting on an exception message to be sent, if the
+ // handler is installed
+ mach_port_t handler_port_;
+
+ // These variables save the previous exception handler's data so that it
+ // can be re-installed when this handler is uninstalled
+ ExceptionParameters *previous_;
+
+ // True, if we've installed the exception handler
+ bool installed_exception_handler_;
+
+ // True, if we're in the process of uninstalling the exception handler and
+ // the thread.
+ bool is_in_teardown_;
+
+ // Save the last result of the last minidump
+ bool last_minidump_write_result_;
+
+ // A mutex for use when writing out a minidump that was requested on a
+ // thread other than the exception handler.
+ pthread_mutex_t minidump_write_mutex_;
+
+ // True, if we're using the mutext to indicate when mindump writing occurs
+ bool use_minidump_write_mutex_;
+
+ // Old signal handler for SIGABRT. Used to be able to restore it when
+ // uninstalling.
+ scoped_ptr<struct sigaction> old_handler_;
+
+#if !TARGET_OS_IPHONE
+ // Client for out-of-process dump generation.
+ scoped_ptr<CrashGenerationClient> crash_generation_client_;
+#endif
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/mach_vm_compat.h b/toolkit/crashreporter/breakpad-client/mac/handler/mach_vm_compat.h
new file mode 100644
index 0000000000..aef3ad82f5
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/mach_vm_compat.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2011, 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.
+
+#ifndef CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
+#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
+
+#include <TargetConditionals.h>
+
+// On iOS 5 and higher, mach/mach_vm.h is not supported. Use the corresponding
+// vm_map functions instead.
+#if TARGET_OS_IPHONE
+#include <mach/vm_map.h>
+#define mach_vm_address_t vm_address_t
+#define mach_vm_deallocate vm_deallocate
+#define mach_vm_read vm_read
+#define mach_vm_region_recurse vm_region_recurse_64
+#define mach_vm_size_t vm_size_t
+#else
+#include <mach/mach_vm.h>
+#endif // TARGET_OS_IPHONE
+
+// This is the current (as of macOS 11.2.3) raw format of the data in the
+// __crash_info section of the __DATA segment. It must be read from memory
+// (__crash_info sections in file system modules are always empty of useful
+// data). The data (and that of its strings) may be readable from an out-of-
+// process image, but generally only if the module is in the dyld shared
+// cache. A __crash_info section is considered "empty of useful data" if all
+// of its fields besides 'version' are zero. The __crash_info section is only
+// present on macOS. Apple may add new fields to this structure in the future,
+// as they added 'abort_cause' when 'version' changed from '4' to '5'.
+// Breakpad should take this into account, and provide a way for new fields to
+// be added to future versions of MDRawMacCrashInfoRecord without breaking
+// older code.
+typedef struct { // Non-raw format
+ uint64_t version; // unsigned long
+ uint64_t message; // char *
+ uint64_t signature_string; // char *
+ uint64_t backtrace; // char *
+ uint64_t message2; // char *
+ // We can't use uint64_t for 'thread' in non-raw structures: Though uint64_t
+ // is the same size on all platforms, it's "unsigned long" on some platforms
+ // (like Android and Linux) and "unsigned long long" on others (like macOS
+ // and Windows).
+ uint64_t thread; // uint64_t, but we use unsigned long long
+ uint64_t dialog_mode; // unsigned int
+ // There's a string in the OSAnalytics private framework ("Abort Cause %lld")
+ // which hints that this field's non-raw format is long long.
+ uint64_t abort_cause; // long long, only present when
+ // 'version' > 4
+} crashreporter_annotations_t;
+
+// Defines for calls to getsectdatafromheader_64() or getsectdatafromheader().
+// __LP64__ is true on both amd64 and arm64 (Apple Silicon) hardware.
+#ifdef __LP64__
+ #define getsectdatafromheader_func getsectdatafromheader_64
+ typedef uint64_t getsectdata_size_type;
+#else
+ #define getsectdatafromheader_func getsectdatafromheader
+ typedef uint32_t getsectdata_size_type;
+#endif
+
+#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc
new file mode 100644
index 0000000000..e13e4509b0
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc
@@ -0,0 +1,2135 @@
+// 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 <algorithm>
+#include <cstdio>
+
+#include <mach/host_info.h>
+#include <mach/machine.h>
+#include <mach/vm_statistics.h>
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "mac/handler/minidump_generator.h"
+
+#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
+#include <mach/arm/thread_status.h>
+#endif
+#ifdef HAS_PPC_SUPPORT
+#include <mach/ppc/thread_status.h>
+#endif
+#ifdef HAS_X86_SUPPORT
+#include <mach/i386/thread_status.h>
+#endif
+
+#include "minidump_file_writer-inl.h"
+#include "common/mac/file_id.h"
+#include "common/mac/macho_id.h"
+#include "common/mac/string_utilities.h"
+
+using MacStringUtils::ConvertToString;
+using MacStringUtils::IntegerValueAtIndex;
+
+namespace google_breakpad {
+
+#if defined(__LP64__) && __LP64__
+#define LC_SEGMENT_ARCH LC_SEGMENT_64
+#define MH_MAGIC_ARCH MH_MAGIC_64
+#else
+#define LC_SEGMENT_ARCH LC_SEGMENT
+#define MH_MAGIC_ARCH MH_MAGIC
+#endif
+
+// constructor when generating from within the crashed process
+MinidumpGenerator::MinidumpGenerator()
+ : writer_(),
+ exception_type_(0),
+ exception_code_(0),
+ exception_subcode_(0),
+ exception_thread_(0),
+ crashing_task_(mach_task_self()),
+ handler_thread_(mach_thread_self()),
+ cpu_type_(DynamicImages::GetNativeCPUType()),
+ dyldImageLoadAddress_(NULL),
+ dyldSlide_(0),
+ dyldPath_(),
+ task_context_(NULL),
+ dynamic_images_(NULL),
+ memory_blocks_(&allocator_) {
+ GatherSystemInformation();
+ GatherCurrentProcessDyldInformation();
+}
+
+// constructor when generating from a different process than the
+// crashed process
+MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
+ mach_port_t handler_thread)
+ : writer_(),
+ exception_type_(0),
+ exception_code_(0),
+ exception_subcode_(0),
+ exception_thread_(0),
+ crashing_task_(crashing_task),
+ handler_thread_(handler_thread),
+ cpu_type_(DynamicImages::GetNativeCPUType()),
+ dyldImageLoadAddress_(NULL),
+ dyldSlide_(0),
+ dyldPath_(),
+ task_context_(NULL),
+ dynamic_images_(NULL),
+ memory_blocks_(&allocator_) {
+ if (crashing_task != mach_task_self()) {
+ dynamic_images_ = new DynamicImages(crashing_task_);
+ cpu_type_ = dynamic_images_->GetCPUType();
+ } else {
+ dynamic_images_ = NULL;
+ cpu_type_ = DynamicImages::GetNativeCPUType();
+ }
+
+ GatherSystemInformation();
+ // This constructor is used when creating a crash server, but the crash
+ // server may also be the crashing process.
+ GatherCurrentProcessDyldInformation();
+}
+
+MinidumpGenerator::~MinidumpGenerator() {
+ delete dynamic_images_;
+}
+
+char MinidumpGenerator::build_string_[16];
+int MinidumpGenerator::os_major_version_ = 0;
+int MinidumpGenerator::os_minor_version_ = 0;
+int MinidumpGenerator::os_build_number_ = 0;
+
+// static
+void MinidumpGenerator::GatherSystemInformation() {
+ // If this is non-zero, then we've already gathered the information
+ if (os_major_version_)
+ return;
+
+ // This code extracts the version and build information from the OS
+ CFStringRef vers_path =
+ CFSTR("/System/Library/CoreServices/SystemVersion.plist");
+ CFURLRef sys_vers =
+ CFURLCreateWithFileSystemPath(NULL,
+ vers_path,
+ kCFURLPOSIXPathStyle,
+ false);
+ CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers);
+ CFRelease(sys_vers);
+ if (!read_stream) {
+ return;
+ }
+ if (!CFReadStreamOpen(read_stream)) {
+ CFRelease(read_stream);
+ return;
+ }
+ CFMutableDataRef data = NULL;
+ while (true) {
+ // Actual data file tests: Mac at 480 bytes and iOS at 413 bytes.
+ const CFIndex kMaxBufferLength = 1024;
+ UInt8 data_bytes[kMaxBufferLength];
+ CFIndex num_bytes_read =
+ CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength);
+ if (num_bytes_read < 0) {
+ if (data) {
+ CFRelease(data);
+ data = NULL;
+ }
+ break;
+ } else if (num_bytes_read == 0) {
+ break;
+ } else if (!data) {
+ data = CFDataCreateMutable(NULL, 0);
+ }
+ CFDataAppendBytes(data, data_bytes, num_bytes_read);
+ }
+ CFReadStreamClose(read_stream);
+ CFRelease(read_stream);
+ if (!data) {
+ return;
+ }
+ CFDictionaryRef list =
+ static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(
+ NULL, data, kCFPropertyListImmutable, NULL, NULL));
+ CFRelease(data);
+ if (!list) {
+ return;
+ }
+ CFStringRef build_version = static_cast<CFStringRef>
+ (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
+ CFStringRef product_version = static_cast<CFStringRef>
+ (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
+ string build_str = ConvertToString(build_version);
+ string product_str = ConvertToString(product_version);
+
+ CFRelease(list);
+
+ strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
+
+ // Parse the string that looks like "10.4.8"
+ os_major_version_ = IntegerValueAtIndex(product_str, 0);
+ os_minor_version_ = IntegerValueAtIndex(product_str, 1);
+ os_build_number_ = IntegerValueAtIndex(product_str, 2);
+}
+
+// static
+uint64_t
+MinidumpGenerator::GetCurrentProcessModuleSlide(breakpad_mach_header* mh,
+ uint64_t shared_cache_slide) {
+ if (!mh || (mh->magic != MH_MAGIC_ARCH)) {
+ return 0;
+ }
+
+ if ((mh->flags & MH_SHAREDCACHE) != 0) {
+ return shared_cache_slide;
+ }
+
+ uint64_t slide = 0;
+
+ uint32_t num_commands = mh->ncmds;
+ breakpad_mach_segment_command* cmd = (breakpad_mach_segment_command*)
+ ((uintptr_t)mh + sizeof(breakpad_mach_header));
+ for (uint32_t i = 0; i < num_commands; ++i) {
+ if (cmd->cmd != LC_SEGMENT_ARCH) {
+ break;
+ }
+ if (!cmd->fileoff && cmd->filesize) {
+ slide = (uintptr_t)mh - cmd->vmaddr;
+ break;
+ }
+ cmd = (breakpad_mach_segment_command*) ((uintptr_t)cmd + cmd->cmdsize);
+ }
+
+ return slide;
+}
+
+void MinidumpGenerator::GatherCurrentProcessDyldInformation() {
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ if (task_info(mach_task_self(), TASK_DYLD_INFO,
+ (task_info_t)&task_dyld_info, &count) != KERN_SUCCESS) {
+ return;
+ }
+
+ dyld_all_image_infos_self* aii = (dyld_all_image_infos_self*)
+ task_dyld_info.all_image_info_addr;
+ breakpad_mach_header* mh = (breakpad_mach_header*) aii->dyldImageLoadAddress;
+ if (!mh || (mh->magic != MH_MAGIC_ARCH)) {
+ return;
+ }
+ dyldImageLoadAddress_ = mh;
+ dyldPath_ = string(aii->dyldPath);
+ dyldSlide_ = GetCurrentProcessModuleSlide(mh, aii->sharedCacheSlide);
+}
+
+void MinidumpGenerator::SetTaskContext(breakpad_ucontext_t *task_context) {
+ task_context_ = task_context;
+}
+
+string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
+ string *unique_name) {
+ CFUUIDRef uuid = CFUUIDCreate(NULL);
+ CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
+ CFRelease(uuid);
+ string file_name(ConvertToString(uuid_cfstr));
+ CFRelease(uuid_cfstr);
+ string path(dir);
+
+ // Ensure that the directory (if non-empty) has a trailing slash so that
+ // we can append the file name and have a valid pathname.
+ if (!dir.empty()) {
+ if (dir.at(dir.size() - 1) != '/')
+ path.append(1, '/');
+ }
+
+ path.append(file_name);
+ path.append(".dmp");
+
+ if (unique_name)
+ *unique_name = file_name;
+
+ return path;
+}
+
+bool MinidumpGenerator::Write(const char *path) {
+ WriteStreamFN writers[] = {
+ &MinidumpGenerator::WriteThreadListStream,
+ &MinidumpGenerator::WriteMemoryListStream,
+ &MinidumpGenerator::WriteSystemInfoStream,
+ &MinidumpGenerator::WriteModuleListStream,
+ &MinidumpGenerator::WriteMiscInfoStream,
+ &MinidumpGenerator::WriteBreakpadInfoStream,
+ &MinidumpGenerator::WriteCrashInfoStream,
+ &MinidumpGenerator::WriteBootargsStream,
+ &MinidumpGenerator::WriteThreadNamesStream,
+ // Exception stream needs to be the last entry in this array as it may
+ // be omitted in the case where the minidump is written without an
+ // exception.
+ &MinidumpGenerator::WriteExceptionStream,
+ };
+ bool result = false;
+
+ // If opening was successful, create the header, directory, and call each
+ // writer. The destructor for the TypedMDRVAs will cause the data to be
+ // flushed. The destructor for the MinidumpFileWriter will close the file.
+ if (writer_.Open(path)) {
+ TypedMDRVA<MDRawHeader> header(&writer_);
+ TypedMDRVA<MDRawDirectory> dir(&writer_);
+
+ if (!header.Allocate())
+ return false;
+
+ int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
+
+ // If we don't have exception information, don't write out the
+ // exception stream
+ if (!exception_thread_ && !exception_type_)
+ --writer_count;
+
+ // Add space for all writers
+ if (!dir.AllocateArray(writer_count))
+ return false;
+
+ MDRawHeader *header_ptr = header.get();
+ header_ptr->signature = MD_HEADER_SIGNATURE;
+ header_ptr->version = MD_HEADER_VERSION;
+ time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
+ header_ptr->stream_count = writer_count;
+ header_ptr->stream_directory_rva = dir.position();
+
+ MDRawDirectory local_dir;
+ result = true;
+ for (int i = 0; (result) && (i < writer_count); ++i) {
+ result = (this->*writers[i])(&local_dir);
+
+ if (result)
+ dir.CopyIndex(i, &local_dir);
+ }
+ }
+ return result;
+}
+
+size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
+ mach_vm_address_t stack_region_base = start_addr;
+ mach_vm_size_t stack_region_size;
+ natural_t nesting_level = 0;
+ vm_region_submap_info_64 submap_info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ vm_region_recurse_info_t region_info;
+ region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
+
+ if (start_addr == 0) {
+ return 0;
+ }
+
+ kern_return_t result =
+ mach_vm_region_recurse(crashing_task_, &stack_region_base,
+ &stack_region_size, &nesting_level,
+ region_info, &info_count);
+
+ if (result != KERN_SUCCESS || start_addr < stack_region_base) {
+ // Failure or stack corruption, since mach_vm_region had to go
+ // higher in the process address space to find a valid region.
+ return 0;
+ }
+
+ unsigned int tag = submap_info.user_tag;
+
+ // If the user tag is VM_MEMORY_STACK, look for more readable regions with
+ // the same tag placed immediately above the computed stack region. Under
+ // some circumstances, the stack for thread 0 winds up broken up into
+ // multiple distinct abutting regions. This can happen for several reasons,
+ // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
+ // the access on stack pages by calling mprotect.
+ if (tag == VM_MEMORY_STACK) {
+ while (true) {
+ mach_vm_address_t next_region_base = stack_region_base +
+ stack_region_size;
+ mach_vm_address_t proposed_next_region_base = next_region_base;
+ mach_vm_size_t next_region_size;
+ nesting_level = 0;
+ info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+ result = mach_vm_region_recurse(crashing_task_, &next_region_base,
+ &next_region_size, &nesting_level,
+ region_info, &info_count);
+ if (result != KERN_SUCCESS ||
+ next_region_base != proposed_next_region_base ||
+ submap_info.user_tag != tag ||
+ (submap_info.protection & VM_PROT_READ) == 0) {
+ break;
+ }
+
+ stack_region_size += next_region_size;
+ }
+ }
+
+ return stack_region_base + stack_region_size - start_addr;
+}
+
+bool MinidumpGenerator::WriteStackFromStartAddress(
+ mach_vm_address_t start_addr,
+ MDMemoryDescriptor *stack_location) {
+ UntypedMDRVA memory(&writer_);
+
+ bool result = false;
+ size_t size = CalculateStackSize(start_addr);
+
+ if (size == 0) {
+ // In some situations the stack address for the thread can come back 0.
+ // In these cases we skip over the threads in question and stuff the
+ // stack with a clearly borked value.
+ start_addr = 0xDEADBEEF;
+ size = 16;
+ if (!memory.Allocate(size))
+ return false;
+
+ unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of
+ // junk.
+ dummy_stack[0] = 0xDEADBEEF;
+ dummy_stack[1] = 0xDEADBEEF;
+
+ result = memory.Copy(dummy_stack, size);
+ } else {
+
+ if (!memory.Allocate(size))
+ return false;
+
+ if (dynamic_images_) {
+ vector<uint8_t> stack_memory;
+ if (ReadTaskMemory(crashing_task_,
+ start_addr,
+ size,
+ stack_memory) != KERN_SUCCESS) {
+ return false;
+ }
+
+ result = memory.Copy(&stack_memory[0], size);
+ } else {
+ result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
+ }
+ }
+
+ stack_location->start_of_memory_range = start_addr;
+ stack_location->memory = memory.location();
+
+ return result;
+}
+
+bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return WriteStackARM(state, stack_location);
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64:
+ return WriteStackARM64(state, stack_location);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return WriteStackPPC(state, stack_location);
+ case CPU_TYPE_POWERPC64:
+ return WriteStackPPC64(state, stack_location);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return WriteStackX86(state, stack_location);
+ case CPU_TYPE_X86_64:
+ return WriteStackX86_64(state, stack_location);
+#endif
+ default:
+ return false;
+ }
+}
+
+bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return WriteContextARM(state, register_location);
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64:
+ return WriteContextARM64(state, register_location);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return WriteContextPPC(state, register_location);
+ case CPU_TYPE_POWERPC64:
+ return WriteContextPPC64(state, register_location);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return WriteContextX86(state, register_location);
+ case CPU_TYPE_X86_64:
+ return WriteContextX86_64(state, register_location);
+#endif
+ default:
+ return false;
+ }
+}
+
+uint64_t MinidumpGenerator::CurrentPCForStack(
+ breakpad_thread_state_data_t state) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return CurrentPCForStackARM(state);
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64:
+ return CurrentPCForStackARM64(state);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return CurrentPCForStackPPC(state);
+ case CPU_TYPE_POWERPC64:
+ return CurrentPCForStackPPC64(state);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return CurrentPCForStackX86(state);
+ case CPU_TYPE_X86_64:
+ return CurrentPCForStackX86_64(state);
+#endif
+ default:
+ assert(0 && "Unknown CPU type!");
+ return 0;
+ }
+}
+
+#ifdef HAS_ARM_SUPPORT
+bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, pc);
+}
+
+bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextARM> context(&writer_);
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextARM *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
+
+#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a])
+
+ context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr);
+ context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc);
+ context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+#undef AddGPR
+
+ return true;
+}
+#endif
+
+#ifdef HAS_ARM64_SUPPORT
+bool MinidumpGenerator::WriteStackARM64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ arm_thread_state64_t *machine_state =
+ reinterpret_cast<arm_thread_state64_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackARM64(breakpad_thread_state_data_t state) {
+ arm_thread_state64_t *machine_state =
+ reinterpret_cast<arm_thread_state64_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, pc);
+}
+
+bool
+MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextARM64_Old> context(&writer_);
+ arm_thread_state64_t *machine_state =
+ reinterpret_cast<arm_thread_state64_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextARM64_Old *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
+
+#define AddGPR(a) \
+ context_ptr->iregs[a] = ARRAY_REGISTER_FROM_THREADSTATE(machine_state, x, a)
+
+ context_ptr->iregs[29] = REGISTER_FROM_THREADSTATE(machine_state, fp);
+ context_ptr->iregs[30] = REGISTER_FROM_THREADSTATE(machine_state, lr);
+ context_ptr->iregs[31] = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ context_ptr->iregs[32] = REGISTER_FROM_THREADSTATE(machine_state, pc);
+ context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+ AddGPR(13);
+ AddGPR(14);
+ AddGPR(15);
+ AddGPR(16);
+ AddGPR(17);
+ AddGPR(18);
+ AddGPR(19);
+ AddGPR(20);
+ AddGPR(21);
+ AddGPR(22);
+ AddGPR(23);
+ AddGPR(24);
+ AddGPR(25);
+ AddGPR(26);
+ AddGPR(27);
+ AddGPR(28);
+#undef AddGPR
+
+ return true;
+}
+#endif
+
+#ifdef HAS_PCC_SUPPORT
+bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, srr0);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, srr0);
+}
+
+bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextPPC> context(&writer_);
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextPPC *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
+
+#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, a))
+#define AddGPR(a) context_ptr->gpr[a] = \
+ static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+
+ AddReg(srr0);
+ AddReg(cr);
+ AddReg(xer);
+ AddReg(ctr);
+ AddReg(lr);
+ AddReg(vrsave);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+ AddGPR(13);
+ AddGPR(14);
+ AddGPR(15);
+ AddGPR(16);
+ AddGPR(17);
+ AddGPR(18);
+ AddGPR(19);
+ AddGPR(20);
+ AddGPR(21);
+ AddGPR(22);
+ AddGPR(23);
+ AddGPR(24);
+ AddGPR(25);
+ AddGPR(26);
+ AddGPR(27);
+ AddGPR(28);
+ AddGPR(29);
+ AddGPR(30);
+ AddGPR(31);
+ AddReg(mq);
+#undef AddReg
+#undef AddGPR
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteContextPPC64(
+ breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextPPC64> context(&writer_);
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextPPC64 *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
+
+#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, a))
+#define AddGPR(a) context_ptr->gpr[a] = \
+ static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+
+ AddReg(srr0);
+ AddReg(cr);
+ AddReg(xer);
+ AddReg(ctr);
+ AddReg(lr);
+ AddReg(vrsave);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+ AddGPR(13);
+ AddGPR(14);
+ AddGPR(15);
+ AddGPR(16);
+ AddGPR(17);
+ AddGPR(18);
+ AddGPR(19);
+ AddGPR(20);
+ AddGPR(21);
+ AddGPR(22);
+ AddGPR(23);
+ AddGPR(24);
+ AddGPR(25);
+ AddGPR(26);
+ AddGPR(27);
+ AddGPR(28);
+ AddGPR(29);
+ AddGPR(30);
+ AddGPR(31);
+#undef AddReg
+#undef AddGPR
+
+ return true;
+}
+
+#endif
+
+#ifdef HAS_X86_SUPPORT
+bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ mach_vm_address_t start_addr = static_cast<mach_vm_address_t>(
+ REGISTER_FROM_THREADSTATE(machine_state, rsp));
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, eip);
+}
+
+uint64_t
+MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, rip);
+}
+
+bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextX86> context(&writer_);
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextX86 *context_ptr = context.get();
+
+#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, a))
+
+ context_ptr->context_flags = MD_CONTEXT_X86;
+ AddReg(eax);
+ AddReg(ebx);
+ AddReg(ecx);
+ AddReg(edx);
+ AddReg(esi);
+ AddReg(edi);
+ AddReg(ebp);
+ AddReg(esp);
+
+ AddReg(cs);
+ AddReg(ds);
+ AddReg(ss);
+ AddReg(es);
+ AddReg(fs);
+ AddReg(gs);
+ AddReg(eflags);
+
+ AddReg(eip);
+#undef AddReg
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteContextX86_64(
+ breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextAMD64> context(&writer_);
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextAMD64 *context_ptr = context.get();
+
+#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
+ REGISTER_FROM_THREADSTATE(machine_state, a))
+
+ context_ptr->context_flags = MD_CONTEXT_AMD64;
+ AddReg(rax);
+ AddReg(rbx);
+ AddReg(rcx);
+ AddReg(rdx);
+ AddReg(rdi);
+ AddReg(rsi);
+ AddReg(rbp);
+ AddReg(rsp);
+ AddReg(r8);
+ AddReg(r9);
+ AddReg(r10);
+ AddReg(r11);
+ AddReg(r12);
+ AddReg(r13);
+ AddReg(r14);
+ AddReg(r15);
+ AddReg(rip);
+ // according to AMD's software developer guide, bits above 18 are
+ // not used in the flags register. Since the minidump format
+ // specifies 32 bits for the flags register, we can truncate safely
+ // with no loss.
+ context_ptr->eflags = static_cast<uint32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
+ AddReg(cs);
+ AddReg(fs);
+ AddReg(gs);
+#undef AddReg
+
+ return true;
+}
+#endif
+
+bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
+ thread_state_t state,
+ mach_msg_type_number_t *count) {
+ if (task_context_ && target_thread == mach_thread_self()) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ size_t final_size =
+ std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t));
+ memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
+ *count = static_cast<mach_msg_type_number_t>(final_size);
+ return true;
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64: {
+ size_t final_size =
+ std::min(static_cast<size_t>(*count), sizeof(arm_thread_state64_t));
+ memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
+ *count = static_cast<mach_msg_type_number_t>(final_size);
+ return true;
+ }
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64: {
+ size_t state_size = cpu_type_ == CPU_TYPE_I386 ?
+ sizeof(i386_thread_state_t) : sizeof(x86_thread_state64_t);
+ size_t final_size =
+ std::min(static_cast<size_t>(*count), state_size);
+ memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
+ *count = static_cast<mach_msg_type_number_t>(final_size);
+ return true;
+ }
+#endif
+ }
+ }
+
+ thread_state_flavor_t flavor;
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ flavor = ARM_THREAD_STATE;
+ break;
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64:
+ flavor = ARM_THREAD_STATE64;
+ break;
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ flavor = PPC_THREAD_STATE;
+ break;
+ case CPU_TYPE_POWERPC64:
+ flavor = PPC_THREAD_STATE64;
+ break;
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ flavor = i386_THREAD_STATE;
+ break;
+ case CPU_TYPE_X86_64:
+ flavor = x86_THREAD_STATE64;
+ break;
+#endif
+ default:
+ return false;
+ }
+ return thread_get_state(target_thread, flavor,
+ state, count) == KERN_SUCCESS;
+}
+
+bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
+ MDRawThread *thread) {
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t state_count
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (GetThreadState(thread_id, state, &state_count)) {
+ if (!WriteStack(state, &thread->stack))
+ return false;
+
+ memory_blocks_.push_back(thread->stack);
+
+ if (!WriteContext(state, &thread->thread_context))
+ return false;
+
+ thread->thread_id = thread_id;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteThreadListStream(
+ MDRawDirectory *thread_list_stream) {
+ TypedMDRVA<MDRawThreadList> list(&writer_);
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+ int non_generator_thread_count;
+
+ if (task_threads(crashing_task_, &threads_for_task, &thread_count))
+ return false;
+
+ // Don't include the generator thread
+ if (handler_thread_ != MACH_PORT_NULL)
+ non_generator_thread_count = thread_count - 1;
+ else
+ non_generator_thread_count = thread_count;
+ if (!list.AllocateObjectAndArray(non_generator_thread_count,
+ sizeof(MDRawThread)))
+ return false;
+
+ thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
+ thread_list_stream->location = list.location();
+
+ list.get()->number_of_threads = non_generator_thread_count;
+
+ MDRawThread thread;
+ int thread_idx = 0;
+
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ memset(&thread, 0, sizeof(MDRawThread));
+
+ if (threads_for_task[i] != handler_thread_) {
+ if (!WriteThreadStream(threads_for_task[i], &thread))
+ return false;
+
+ list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
+ }
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteMemoryListStream(
+ MDRawDirectory *memory_list_stream) {
+ TypedMDRVA<MDRawMemoryList> list(&writer_);
+
+ // If the dump has an exception, include some memory around the
+ // instruction pointer.
+ const size_t kIPMemorySize = 256; // bytes
+ bool have_ip_memory = false;
+ MDMemoryDescriptor ip_memory_d;
+ if (exception_thread_ && exception_type_) {
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t stateCount
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (GetThreadState(exception_thread_, state, &stateCount)) {
+ uint64_t ip = CurrentPCForStack(state);
+ // Bound it to the upper and lower bounds of the region
+ // it's contained within. If it's not in a known memory region,
+ // don't bother trying to write it.
+ mach_vm_address_t addr = static_cast<vm_address_t>(ip);
+ mach_vm_size_t size;
+ natural_t nesting_level = 0;
+ vm_region_submap_info_64 info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+ vm_region_recurse_info_t recurse_info;
+ recurse_info = reinterpret_cast<vm_region_recurse_info_t>(&info);
+
+ kern_return_t ret =
+ mach_vm_region_recurse(crashing_task_,
+ &addr,
+ &size,
+ &nesting_level,
+ recurse_info,
+ &info_count);
+ if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
+ // Try to get 128 bytes before and after the IP, but
+ // settle for whatever's available.
+ ip_memory_d.start_of_memory_range =
+ std::max(uintptr_t(addr),
+ uintptr_t(ip - (kIPMemorySize / 2)));
+ uintptr_t end_of_range =
+ std::min(uintptr_t(ip + (kIPMemorySize / 2)),
+ uintptr_t(addr + size));
+ uintptr_t range_diff = end_of_range -
+ static_cast<uintptr_t>(ip_memory_d.start_of_memory_range);
+ ip_memory_d.memory.data_size = static_cast<uint32_t>(range_diff);
+ have_ip_memory = true;
+ // This needs to get appended to the list even though
+ // the memory bytes aren't filled in yet so the entire
+ // list can be written first. The memory bytes will get filled
+ // in after the memory list is written.
+ memory_blocks_.push_back(ip_memory_d);
+ }
+ }
+ }
+
+ // Now fill in the memory list and write it.
+ size_t memory_count = memory_blocks_.size();
+ if (!list.AllocateObjectAndArray(memory_count,
+ sizeof(MDMemoryDescriptor)))
+ return false;
+
+ memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
+ memory_list_stream->location = list.location();
+
+ list.get()->number_of_memory_ranges = static_cast<uint32_t>(memory_count);
+
+ unsigned int i;
+ for (i = 0; i < memory_count; ++i) {
+ list.CopyIndexAfterObject(i, &memory_blocks_[i],
+ sizeof(MDMemoryDescriptor));
+ }
+
+ if (have_ip_memory) {
+ // Now read the memory around the instruction pointer.
+ UntypedMDRVA ip_memory(&writer_);
+ if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
+ return false;
+
+ if (dynamic_images_) {
+ // Out-of-process.
+ vector<uint8_t> memory;
+ if (ReadTaskMemory(crashing_task_,
+ ip_memory_d.start_of_memory_range,
+ ip_memory_d.memory.data_size,
+ memory) != KERN_SUCCESS) {
+ return false;
+ }
+
+ ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
+ } else {
+ // In-process, just copy from local memory.
+ ip_memory.Copy(
+ reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
+ ip_memory_d.memory.data_size);
+ }
+
+ ip_memory_d.memory = ip_memory.location();
+ // Write this again now that the data location is filled in.
+ list.CopyIndexAfterObject(i - 1, &ip_memory_d,
+ sizeof(MDMemoryDescriptor));
+ }
+
+ return true;
+}
+
+bool
+MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
+ TypedMDRVA<MDRawExceptionStream> exception(&writer_);
+
+ if (!exception.Allocate())
+ return false;
+
+ exception_stream->stream_type = MD_EXCEPTION_STREAM;
+ exception_stream->location = exception.location();
+ MDRawExceptionStream *exception_ptr = exception.get();
+ exception_ptr->thread_id = exception_thread_;
+
+ uint64_t u_exception_code = exception_code_;
+ if (exception_type_ == EXC_CRASH) {
+ if (!IsValidExcCrash(exception_code_)) {
+ return false;
+ }
+
+ [[maybe_unused]] int signal_number;
+ RecoverExceptionDataFromExcCrash(u_exception_code, signal_number);
+ }
+
+ // This naming is confusing, but it is the proper translation from
+ // mach naming to minidump naming.
+ exception_ptr->exception_record.exception_code = exception_type_;
+
+ uint32_t exception_flags = 0;
+ if (exception_type_ == EXC_RESOURCE || exception_type_ == EXC_GUARD) {
+ // For EXC_RESOURCE and EXC_GUARD crashes Crashpad records the uppermost
+ // 32 bits of the exception code in the exception flags, let's do the same
+ // here.
+ exception_flags = u_exception_code >> 32;
+ } else {
+ exception_flags = exception_code_;
+ }
+
+ exception_ptr->exception_record.exception_flags = exception_flags;
+
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t state_count
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (!GetThreadState(exception_thread_, state, &state_count))
+ return false;
+
+ if (!WriteContext(state, &exception_ptr->thread_context))
+ return false;
+
+ if (exception_type_ == EXC_BAD_ACCESS)
+ exception_ptr->exception_record.exception_address = exception_subcode_;
+ else
+ exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
+
+ // Crashpad stores the exception type and the optional exception codes in
+ // the exception information field, so we do the same here.
+ exception_ptr->exception_record.number_parameters =
+ (exception_subcode_ != 0) ? 3 : 2;
+ exception_ptr->exception_record.exception_information[0] = exception_type_;
+ exception_ptr->exception_record.exception_information[1] = exception_code_;
+ exception_ptr->exception_record.exception_information[2] = exception_subcode_;
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteSystemInfoStream(
+ MDRawDirectory *system_info_stream) {
+ TypedMDRVA<MDRawSystemInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
+ system_info_stream->location = info.location();
+
+ // CPU Information
+ uint32_t number_of_processors;
+ size_t len = sizeof(number_of_processors);
+ sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
+ MDRawSystemInfo *info_ptr = info.get();
+
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
+ break;
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ case CPU_TYPE_ARM64:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM64_OLD;
+ break;
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
+ break;
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ if (cpu_type_ == CPU_TYPE_I386)
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
+ else
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
+#ifdef __i386__
+ // ebx is used for PIC code, so we need
+ // to preserve it.
+#define cpuid(op,eax,ebx,ecx,edx) \
+ asm ("pushl %%ebx \n\t" \
+ "cpuid \n\t" \
+ "movl %%ebx,%1 \n\t" \
+ "popl %%ebx" \
+ : "=a" (eax), \
+ "=g" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#elif defined(__x86_64__)
+
+#define cpuid(op,eax,ebx,ecx,edx) \
+ asm ("cpuid \n\t" \
+ : "=a" (eax), \
+ "=b" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+ int unused, unused2;
+ // get vendor id
+ cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
+ info_ptr->cpu.x86_cpu_info.vendor_id[2],
+ info_ptr->cpu.x86_cpu_info.vendor_id[1]);
+ // get version and feature info
+ cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
+ info_ptr->cpu.x86_cpu_info.feature_information);
+
+ // family
+ info_ptr->processor_level =
+ (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
+ // 0xMMSS (Model, Stepping)
+ info_ptr->processor_revision = static_cast<uint16_t>(
+ (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4));
+
+ // decode extended model info
+ if (info_ptr->processor_level == 0xF ||
+ info_ptr->processor_level == 0x6) {
+ info_ptr->processor_revision |=
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
+ }
+
+ // decode extended family info
+ if (info_ptr->processor_level == 0xF) {
+ info_ptr->processor_level +=
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
+ }
+
+#endif // __i386__ || __x86_64_
+ break;
+#endif // HAS_X86_SUPPORT
+ default:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
+ break;
+ }
+
+ info_ptr->number_of_processors = static_cast<uint8_t>(number_of_processors);
+#if TARGET_OS_IPHONE
+ info_ptr->platform_id = MD_OS_IOS;
+#else
+ info_ptr->platform_id = MD_OS_MAC_OS_X;
+#endif // TARGET_OS_IPHONE
+
+ MDLocationDescriptor build_string_loc;
+
+ if (!writer_.WriteString(build_string_, 0,
+ &build_string_loc))
+ return false;
+
+ info_ptr->csd_version_rva = build_string_loc.rva;
+ info_ptr->major_version = os_major_version_;
+ info_ptr->minor_version = os_minor_version_;
+ info_ptr->build_number = os_build_number_;
+
+ return true;
+}
+
+// If index == INT_MAX, we're being asked to write dyld in the crashed process.
+bool MinidumpGenerator::WriteModuleStream(unsigned int index,
+ MDRawModule *module) {
+ if (dynamic_images_) {
+ // we're in a different process than the crashed process
+ DynamicImage *image = dynamic_images_->GetImage(index);
+
+ if (!image)
+ return false;
+
+ memset(module, 0, sizeof(MDRawModule));
+
+ MDLocationDescriptor string_location;
+
+ string name = image->GetFilePath();
+ if (!writer_.WriteString(name.c_str(), 0, &string_location))
+ return false;
+
+ module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
+ module->size_of_image = static_cast<uint32_t>(image->GetVMSize());
+ module->module_name_rva = string_location.rva;
+
+ // We'll skip the executable module, because they don't have
+ // LC_ID_DYLIB load commands, and the crash processing server gets
+ // version information from the Plist file, anyway.
+ if (index != static_cast<uint32_t>(FindExecutableModule())) {
+ module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
+ module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
+ // Convert MAC dylib version format, which is a 32 bit number, to the
+ // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits>
+ // so it fits nicely into the windows version with some massaging
+ // The mapping is:
+ // 1) upper 16 bits of MAC version go to lower 16 bits of product HI
+ // 2) Next most significant 8 bits go to upper 16 bits of product LO
+ // 3) Least significant 8 bits go to lower 16 bits of product LO
+ uint32_t modVersion = image->GetVersion();
+ module->version_info.file_version_hi = 0;
+ module->version_info.file_version_hi = modVersion >> 16;
+ module->version_info.file_version_lo |= (modVersion & 0xff00) << 8;
+ module->version_info.file_version_lo |= (modVersion & 0xff);
+ }
+
+ if (!WriteCVRecord(module, image->GetCPUType(), image->GetCPUSubtype(),
+ name.c_str(), /* in_memory */ false, /* out_of_process */ true,
+ image->GetIsDyld() || image->GetInDyldSharedCache())) {
+ return false;
+ }
+ } else {
+ // Getting module info in the crashed process
+ const breakpad_mach_header *header;
+ if (index == INT_MAX) {
+ header = dyldImageLoadAddress_;
+ } else {
+ header = (breakpad_mach_header*)_dyld_get_image_header(index);
+ }
+ if (!header)
+ return false;
+
+#ifdef __LP64__
+ assert(header->magic == MH_MAGIC_64);
+
+ if(header->magic != MH_MAGIC_64)
+ return false;
+#else
+ assert(header->magic == MH_MAGIC);
+
+ if(header->magic != MH_MAGIC)
+ return false;
+#endif
+
+ int cpu_type = header->cputype;
+ int cpu_subtype = (header->cpusubtype & ~CPU_SUBTYPE_MASK);
+ bool dyld_or_in_dyld_shared_cache;
+ unsigned long slide;
+ const char* name;
+ if (index == INT_MAX) {
+ dyld_or_in_dyld_shared_cache = true;
+ slide = dyldSlide_;
+ name = dyldPath_.c_str();
+ } else {
+ dyld_or_in_dyld_shared_cache =
+ ((header->flags & MH_SHAREDCACHE) != 0);
+ slide = _dyld_get_image_vmaddr_slide(index);
+ name = _dyld_get_image_name(index);
+ }
+ const struct load_command *cmd =
+ reinterpret_cast<const struct load_command *>(header + 1);
+
+ memset(module, 0, sizeof(MDRawModule));
+
+ for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
+ if (cmd->cmd == LC_SEGMENT_ARCH) {
+
+ const breakpad_mach_segment_command *seg =
+ reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
+
+ if (!strcmp(seg->segname, "__TEXT")) {
+ MDLocationDescriptor string_location;
+
+ if (!writer_.WriteString(name, 0, &string_location))
+ return false;
+
+ module->base_of_image = seg->vmaddr + slide;
+ module->size_of_image = static_cast<uint32_t>(seg->vmsize);
+ module->module_name_rva = string_location.rva;
+
+ bool in_memory = false;
+#if TARGET_OS_IPHONE
+ in_memory = true;
+#endif
+ if (!WriteCVRecord(module, cpu_type, cpu_subtype, name, in_memory,
+ /* out_of_process */ false,
+ dyld_or_in_dyld_shared_cache)) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
+ }
+ }
+
+ return true;
+}
+
+int MinidumpGenerator::FindExecutableModule() {
+ if (dynamic_images_) {
+ int index = dynamic_images_->GetExecutableImageIndex();
+
+ if (index >= 0) {
+ return index;
+ }
+ } else {
+ int image_count = _dyld_image_count();
+ const struct mach_header *header;
+
+ for (int index = 0; index < image_count; ++index) {
+ header = _dyld_get_image_header(index);
+
+ if (header->filetype == MH_EXECUTE)
+ return index;
+ }
+ }
+
+ // failed - just use the first image
+ return 0;
+}
+
+bool MinidumpGenerator::IsValidExcCrash(uint64_t exception_code) {
+ switch ((exception_code >> 20) & 0xf) {
+ case EXC_CRASH: // EXC_CRASH cannot wrap EXC_CRASH
+ case EXC_RESOURCE: // EXC_RESOURCE would lose data if wrapped
+ case EXC_GUARD: // EXC_GUARD would lose data if wrapped
+ case EXC_CORPSE_NOTIFY: // EXC_CRASH cannot wrap EXC_CORPSE_NOTIFY
+ return false;
+ default:
+ return true;
+ }
+}
+
+void MinidumpGenerator::RecoverExceptionDataFromExcCrash(
+ uint64_t exception_code, int& signal_number)
+{
+ exception_type_ = (exception_code >> 20) & 0xf;
+ exception_code_ = exception_code & 0xfffff;
+ signal_number = (exception_code >> 24) & 0xff;
+}
+
+bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, int cpu_subtype,
+ const char *module_path, bool in_memory,
+ bool out_of_process, bool dyld_or_in_dyld_shared_cache) {
+ TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
+
+ // Only return the last path component of the full module path
+ const char *module_name = strrchr(module_path, '/');
+
+ // Increment past the slash
+ if (module_name)
+ ++module_name;
+ else
+ module_name = "<Unknown>";
+
+ size_t module_name_length = strlen(module_name);
+
+ if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t)))
+ return false;
+
+ if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
+ return false;
+
+ module->cv_record = cv.location();
+ MDCVInfoPDB70 *cv_ptr = cv.get();
+ cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
+ cv_ptr->age = 0;
+
+ // Get the module identifier
+ unsigned char identifier[16];
+ bool result = false;
+ bool in_memory_changed = false;
+ // As of macOS 11, most system libraries no longer have separate copies in
+ // the macOS file system. They only exist all lumped together in the "dyld
+ // shared cache", which gets loaded into each process on startup. If one of
+ // our system libraries isn't in the file system, we can only get a UUID
+ // (aka a debug id) for it by looking at a copy of the module loaded into
+ // the crashing process. Setting 'in_memory' to 'true' makes this happen.
+ //
+ // We should be reluctant to change the value of 'in_memory' from 'false' to
+ // 'true'. But we'll sometimes need to do that to work around the problem
+ // discussed above. In any case we only do it if all else has failed. This
+ // resolves https://bugzilla.mozilla.org/show_bug.cgi?id=1662862.
+ //
+ // We're always called in the main process. But the crashing process might
+ // be either the same process or a different one (a child process). If it's
+ // a child process, the modules we'll be looking at are in that process's
+ // memory space, to which we generally don't have access. But because dyld
+ // and the dyld shared cache are loaded into all processes, we do have
+ // access (in child processes) to dyld and modules in the dyld shared cache.
+ // So it's fine to look at these modules. But we must prevent ourselves from
+ // trying to access other child process modules. This resolves
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1676102.
+ while (true) {
+ if (in_memory) {
+ if (out_of_process && !dyld_or_in_dyld_shared_cache) {
+ break;
+ }
+ MacFileUtilities::MachoID macho(module_path,
+ reinterpret_cast<void *>(module->base_of_image),
+ static_cast<size_t>(module->size_of_image));
+ result = macho.UUIDCommand(cpu_type, cpu_subtype, identifier);
+ if (!result)
+ result = macho.MD5(cpu_type, cpu_subtype, identifier);
+ if (result || in_memory_changed)
+ break;
+ }
+
+ if (!result) {
+ FileID file_id(module_path);
+ result = file_id.MachoIdentifier(cpu_type, cpu_subtype,
+ identifier);
+ }
+ if (result)
+ break;
+
+ if (!in_memory) {
+ in_memory = true;
+ in_memory_changed = true;
+ } else
+ break;
+ }
+
+ if (result) {
+ cv_ptr->signature.data1 =
+ static_cast<uint32_t>(identifier[0]) << 24 |
+ static_cast<uint32_t>(identifier[1]) << 16 |
+ static_cast<uint32_t>(identifier[2]) << 8 |
+ static_cast<uint32_t>(identifier[3]);
+ cv_ptr->signature.data2 =
+ static_cast<uint16_t>(identifier[4] << 8) | identifier[5];
+ cv_ptr->signature.data3 =
+ static_cast<uint16_t>(identifier[6] << 8) | identifier[7];
+ cv_ptr->signature.data4[0] = identifier[8];
+ cv_ptr->signature.data4[1] = identifier[9];
+ cv_ptr->signature.data4[2] = identifier[10];
+ cv_ptr->signature.data4[3] = identifier[11];
+ cv_ptr->signature.data4[4] = identifier[12];
+ cv_ptr->signature.data4[5] = identifier[13];
+ cv_ptr->signature.data4[6] = identifier[14];
+ cv_ptr->signature.data4[7] = identifier[15];
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteModuleListStream(
+ MDRawDirectory *module_list_stream) {
+ TypedMDRVA<MDRawModuleList> list(&writer_);
+
+ uint32_t image_count = dynamic_images_ ?
+ dynamic_images_->GetImageCount() :
+ _dyld_image_count();
+
+ // module_count is one higher when we're in the crashed process, to make
+ // room for dyld, which isn't in the standard list of modules. If
+ // dynamic_images_ exists (and we're in a different process than the
+ // crashed process), we've already added dyld to it.
+ uint32_t module_count = dynamic_images_ ? image_count : image_count + 1;
+
+ if (!list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE))
+ return false;
+
+ module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
+ module_list_stream->location = list.location();
+ list.get()->number_of_modules = static_cast<uint32_t>(module_count);
+
+ // Write out the executable module as the first one
+ MDRawModule module;
+ uint32_t executableIndex = FindExecutableModule();
+
+ if (!WriteModuleStream(static_cast<unsigned>(executableIndex), &module)) {
+ return false;
+ }
+
+ list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
+ int destinationIndex = 1; // Write all other modules after this one
+
+ if (!dynamic_images_) {
+ // If we're in the crashed process we need to write dyld explicitly,
+ // since it's not included in the standard list. index == INT_MAX signals
+ // our intentions to WriteModuleStream().
+ if (!WriteModuleStream(INT_MAX, &module)) {
+ return false;
+ }
+
+ list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
+ }
+
+ for (uint32_t i = 0; i < image_count; ++i) {
+ if (i != executableIndex) {
+ if (!WriteModuleStream(static_cast<unsigned>(i), &module)) {
+ return false;
+ }
+
+ list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
+ }
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
+ TypedMDRVA<MDRawMiscInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
+ misc_info_stream->location = info.location();
+
+ MDRawMiscInfo *info_ptr = info.get();
+ info_ptr->size_of_info = static_cast<uint32_t>(sizeof(MDRawMiscInfo));
+ info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
+ MD_MISCINFO_FLAGS1_PROCESS_TIMES |
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
+
+ // Process ID
+ info_ptr->process_id = getpid();
+
+ // Times
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) != -1) {
+ // Omit the fractional time since the MDRawMiscInfo only wants seconds
+ info_ptr->process_user_time =
+ static_cast<uint32_t>(usage.ru_utime.tv_sec);
+ info_ptr->process_kernel_time =
+ static_cast<uint32_t>(usage.ru_stime.tv_sec);
+ }
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
+ static_cast<int>(info_ptr->process_id) };
+ uint mibsize = static_cast<uint>(sizeof(mib) / sizeof(mib[0]));
+ struct kinfo_proc proc;
+ size_t size = sizeof(proc);
+ if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) {
+ info_ptr->process_create_time =
+ static_cast<uint32_t>(proc.kp_proc.p_starttime.tv_sec);
+ }
+
+ // Speed
+ uint64_t speed;
+ const uint64_t kOneMillion = 1000 * 1000;
+ size = sizeof(speed);
+ sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
+ info_ptr->processor_max_mhz = static_cast<uint32_t>(speed / kOneMillion);
+ info_ptr->processor_mhz_limit = static_cast<uint32_t>(speed / kOneMillion);
+ size = sizeof(speed);
+ sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
+ info_ptr->processor_current_mhz = static_cast<uint32_t>(speed / kOneMillion);
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteBreakpadInfoStream(
+ MDRawDirectory *breakpad_info_stream) {
+ TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
+ breakpad_info_stream->location = info.location();
+ MDRawBreakpadInfo *info_ptr = info.get();
+
+ if (exception_thread_ && exception_type_) {
+ info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
+ info_ptr->dump_thread_id = handler_thread_;
+ info_ptr->requesting_thread_id = exception_thread_;
+ } else {
+ info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
+ info_ptr->dump_thread_id = handler_thread_;
+ info_ptr->requesting_thread_id = 0;
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteCrashInfoRecord(MDLocationDescriptor *location,
+ const char *module_path,
+ const char *crash_info,
+ unsigned long crash_info_size,
+ bool out_of_process,
+ bool dyld_or_in_dyld_shared_cache) {
+ TypedMDRVA<MDRawMacCrashInfoRecord> info(&writer_);
+
+ // Only write crash info records for modules that actually have
+ // __DATA,__crash_info sections.
+ if (!crash_info || !crash_info_size) {
+ return false;
+ }
+ // We generally don't have access to modules in another process's memory
+ // space if the module isn't dyld and isn't in the dyld shared cache.
+ if (out_of_process && !dyld_or_in_dyld_shared_cache) {
+ return false;
+ }
+
+ // If 'crash_info_size' is larger than we expect, 'crash_info' probably
+ // contains fields we don't recognize (added by Apple since we last updated
+ // this code). In that case only copy the fields we do recognize. If it's
+ // smaller than we expect, we're probably running on an older version of
+ // macOS, whose __crash_info sections don't contain all the fields we
+ // recognize. In that case make sure the "missing" fields are zeroed in
+ // 'raw_crash_info'.
+ crashreporter_annotations_t raw_crash_info;
+ bzero(&raw_crash_info, sizeof(raw_crash_info));
+ if (crash_info_size > sizeof(raw_crash_info)) {
+ crash_info_size = sizeof(raw_crash_info);
+ }
+ memcpy(&raw_crash_info, crash_info, crash_info_size);
+
+ // Don't write crash info records that are empty of useful data (see
+ // definition of crashreporter_annotations_t in mach_vm_compat.h).
+ bool is_empty = true;
+ if (raw_crash_info.message ||
+ raw_crash_info.signature_string ||
+ raw_crash_info.backtrace ||
+ raw_crash_info.message2 ||
+ raw_crash_info.thread ||
+ raw_crash_info.dialog_mode ||
+ ((raw_crash_info.version > 4) && raw_crash_info.abort_cause)) {
+ is_empty = false;
+ }
+ if (is_empty) {
+ return false;
+ }
+
+ string message;
+ string signature_string;
+ string backtrace;
+ string message2;
+
+ const char *message_ptr = NULL;
+ const char *signature_string_ptr = NULL;
+ const char *backtrace_ptr = NULL;
+ const char *message2_ptr = NULL;
+
+ if (out_of_process) {
+ if (raw_crash_info.message) {
+ message = ReadTaskString(crashing_task_, raw_crash_info.message);
+ message_ptr = message.c_str();
+ }
+ if (raw_crash_info.signature_string) {
+ signature_string =
+ ReadTaskString(crashing_task_, raw_crash_info.signature_string);
+ signature_string_ptr = signature_string.c_str();
+ }
+ if (raw_crash_info.backtrace) {
+ backtrace = ReadTaskString(crashing_task_, raw_crash_info.backtrace);
+ backtrace_ptr = backtrace.c_str();
+ }
+ if (raw_crash_info.message2) {
+ message2 = ReadTaskString(crashing_task_, raw_crash_info.message2);
+ message2_ptr = message2.c_str();
+ }
+ } else {
+ message_ptr = reinterpret_cast<const char *>(raw_crash_info.message);
+ signature_string_ptr =
+ reinterpret_cast<const char *>(raw_crash_info.signature_string);
+ backtrace_ptr = reinterpret_cast<const char *>(raw_crash_info.backtrace);
+ message2_ptr = reinterpret_cast<const char *>(raw_crash_info.message2);
+ }
+
+ const char* data_strings[] = { module_path, message_ptr,
+ signature_string_ptr, backtrace_ptr,
+ message2_ptr };
+
+ // Compute the total size of the strings we'll be copying to
+ // (MDRawMacCrashInfoRecord).data, including their terminal nulls.
+ size_t data_size = 0;
+ for (auto src : data_strings) {
+ if (!src) {
+ src = "";
+ }
+ // Always include the terminal null, even for an empty string.
+ size_t copy_length = strlen(src) + 1;
+ // A "string" that's too large is a sign of data corruption.
+ if (copy_length > MACCRASHINFO_STRING_MAXSIZE) {
+ return false;
+ }
+ data_size += copy_length;
+ }
+
+ if (!info.AllocateObjectAndArray(data_size, sizeof(uint8_t)))
+ return false;
+
+ // Now copy 'module_path' and the __crash_info strings in order to
+ // (MDRawMacCrashInfoRecord).data, including their terminal nulls.
+ size_t offset = 0;
+ for (auto src : data_strings) {
+ if (!src) {
+ src = "";
+ }
+ // Always include the terminal null, even for an empty string.
+ size_t copy_length = strlen(src) + 1;
+ // We can't use CopyIndexAfterObject() here. Calling that method multiple
+ // times only works for objects in an array (which are all the same size).
+ if (!info.Copy(info.position() + sizeof(MDRawMacCrashInfoRecord) + offset,
+ src, copy_length)) {
+ return false;
+ }
+ offset += copy_length;
+ }
+
+ *location = info.location();
+ MDRawMacCrashInfoRecord *info_ptr = info.get();
+ info_ptr->stream_type = MOZ_MACOS_CRASH_INFO_STREAM;
+ info_ptr->version = raw_crash_info.version;
+ info_ptr->thread = raw_crash_info.thread;
+ info_ptr->dialog_mode = raw_crash_info.dialog_mode;
+ info_ptr->abort_cause = raw_crash_info.abort_cause;
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteCrashInfoStream(
+ MDRawDirectory *crash_info_stream) {
+ TypedMDRVA<MDRawMacCrashInfo> list(&writer_);
+
+ if (!list.Allocate())
+ return false;
+
+ crash_info_stream->stream_type = MOZ_MACOS_CRASH_INFO_STREAM;
+ crash_info_stream->location = list.location();
+
+ MDRawMacCrashInfo *list_ptr = list.get();
+ bzero(list_ptr, sizeof(MDRawMacCrashInfo));
+ list_ptr->stream_type = MOZ_MACOS_CRASH_INFO_STREAM;
+ list_ptr->record_start_size = sizeof(MDRawMacCrashInfoRecord);
+
+ uint32_t image_count = dynamic_images_ ?
+ dynamic_images_->GetImageCount() :
+ // Leave room for dyld, which isn't among the images
+ // counted by _dyld_image_count().
+ _dyld_image_count() + 1;
+ uint32_t crash_info_count = 0;
+ for (uint32_t i = 0; (i < image_count) &&
+ (crash_info_count < MAC_CRASH_INFOS_MAX); ++i) {
+ if (dynamic_images_) {
+ // We're in a different process than the crashed process
+ DynamicImage *image = dynamic_images_->GetImage(i);
+ if (!image) {
+ continue;
+ }
+
+ MDLocationDescriptor location;
+ string module_path = image->GetFilePath();
+ // WriteCrashInfoRecord() fails if a module doesn't contain a
+ // __DATA,__crash_info section, or if it's empty of useful data.
+ if (WriteCrashInfoRecord(&location,
+ module_path.c_str(),
+ reinterpret_cast<const char *>
+ (image->GetCrashInfo()),
+ image->GetCrashInfoSize(),
+ /* out_of_process */ true,
+ image->GetInDyldSharedCache() ||
+ image->GetIsDyld())) {
+ list_ptr->records[crash_info_count] = location;
+ ++crash_info_count;
+ }
+ } else {
+ // Getting crash info in the crashed process
+ const breakpad_mach_header *header;
+ // dyld isn't in our list of images, so tack it onto the end.
+ if (i == image_count - 1) {
+ header = dyldImageLoadAddress_;
+ } else {
+ header = (breakpad_mach_header*) _dyld_get_image_header(i);
+ }
+ if (!header || (header->magic != MH_MAGIC_ARCH)) {
+ continue;
+ }
+
+ unsigned long slide;
+ const char *module_path;
+ bool dyld_or_in_dyld_shared_cache;
+ if (i == image_count - 1) {
+ slide = dyldSlide_;
+ module_path = dyldPath_.c_str();
+ dyld_or_in_dyld_shared_cache = true;
+ } else {
+ slide = _dyld_get_image_vmaddr_slide(i);
+ module_path = _dyld_get_image_name(i);
+ dyld_or_in_dyld_shared_cache =
+ ((header->flags & MH_SHAREDCACHE) != 0);
+ }
+
+ getsectdata_size_type crash_info_size = 0;
+ const char *crash_info =
+ getsectdatafromheader_func(header, "__DATA", "__crash_info",
+ &crash_info_size);
+ // __crash_info might be in the __DATA_DIRTY segment.
+ if (!crash_info) {
+ crash_info =
+ getsectdatafromheader_func(header, "__DATA_DIRTY", "__crash_info",
+ &crash_info_size);
+ }
+ if (crash_info) {
+ crash_info += slide;
+ }
+ MDLocationDescriptor location;
+ // WriteCrashInfoRecord() fails if a module doesn't contain a
+ // __DATA,__crash_info section, or if it's empty of useful data.
+ if (WriteCrashInfoRecord(&location, module_path, crash_info,
+ crash_info_size, /* out_of_process */ false,
+ dyld_or_in_dyld_shared_cache)) {
+ list_ptr->records[crash_info_count] = location;
+ ++crash_info_count;
+ }
+ }
+ }
+
+ list_ptr->record_count = crash_info_count;
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteBootargsStream(
+ MDRawDirectory *bootargs_stream) {
+ TypedMDRVA<MDRawMacBootargs> info(&writer_);
+ if (!info.Allocate())
+ return false;
+
+ bootargs_stream->stream_type = MOZ_MACOS_BOOTARGS_STREAM;
+ bootargs_stream->location = info.location();
+
+ // We need to write *something* to the stream -- otherwise it will get
+ // corrupted. So if we fail to get kern.bootargs, or if it's empty, write
+ // an empty string -- a single terminal null.
+ size_t size = 0; // Includes terminal null
+ int rv = sysctlbyname("kern.bootargs", NULL, &size, NULL, 0);
+ if ((rv != 0) || (size == 0))
+ size = 1;
+ vector<uint8_t> bootargs(size);
+ bootargs[0] = 0;
+ if (rv == 0)
+ sysctlbyname("kern.bootargs", &bootargs[0], &size, NULL, 0);
+
+ MDLocationDescriptor bootargs_location;
+ const char *bootargs_ptr = reinterpret_cast<const char *>(&bootargs[0]);
+ if (!writer_.WriteString(bootargs_ptr, 0, &bootargs_location))
+ return false;
+
+ MDRawMacBootargs *info_ptr = info.get();
+ info_ptr->stream_type = MOZ_MACOS_BOOTARGS_STREAM;
+ info_ptr->bootargs = bootargs_location.rva;
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteThreadName(
+ mach_port_t thread_id,
+ MDRawThreadName *thread_name) {
+ MDLocationDescriptor string_location;
+
+ thread_extended_info_data_t thread_extended_info;
+ mach_msg_type_number_t thread_extended_info_count =
+ THREAD_EXTENDED_INFO_COUNT;
+ kern_return_t res = thread_info(thread_id, THREAD_EXTENDED_INFO,
+ (thread_info_t)&thread_extended_info,
+ &thread_extended_info_count);
+
+ if (res != KERN_SUCCESS)
+ return false;
+
+ if (!writer_.WriteString(thread_extended_info.pth_name, 0, &string_location))
+ return false;
+
+ thread_name->thread_id = thread_id;
+ thread_name->rva_of_thread_name = string_location.rva;
+ return true;
+}
+
+bool MinidumpGenerator::WriteThreadNamesStream(
+ MDRawDirectory *thread_names_stream) {
+ TypedMDRVA<MDRawThreadNamesList> list(&writer_);
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(crashing_task_, &threads_for_task, &thread_count))
+ return false;
+
+ int non_generator_thread_count;
+
+ // Don't include the generator thread
+ if (handler_thread_ != MACH_PORT_NULL)
+ non_generator_thread_count = thread_count - 1;
+ else
+ non_generator_thread_count = thread_count;
+
+ if (!list.AllocateObjectAndArray(non_generator_thread_count,
+ sizeof(MDRawThreadName))) {
+ return false;
+ }
+
+ thread_names_stream->stream_type = MD_THREAD_NAMES_STREAM;
+ thread_names_stream->location = list.location();
+
+ list.get()->number_of_thread_names = non_generator_thread_count;
+
+ MDRawThreadName thread_name;
+ int thread_idx = 0;
+
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ memset(&thread_name, 0, sizeof(MDRawThreadName));
+
+ if (threads_for_task[i] != handler_thread_) {
+ if (WriteThreadName(threads_for_task[i], &thread_name)) {
+ list.CopyIndexAfterObject(thread_idx++, &thread_name,
+ sizeof(MDRawThreadName));
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h
new file mode 100644
index 0000000000..aba067cc04
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h
@@ -0,0 +1,290 @@
+// 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_generator.h: Create a minidump of the current MacOS process.
+
+#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
+#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
+
+#include <mach/mach.h>
+#include <TargetConditionals.h>
+
+#include <string>
+
+#include "mac/handler/ucontext_compat.h"
+#include "minidump_file_writer.h"
+#include "common/memory_allocator.h"
+#include "common/mac/macho_utilities.h"
+#include "google_breakpad/common/minidump_format.h"
+
+#include "dynamic_images.h"
+#include "mach_vm_compat.h"
+
+#if defined(__arm__)
+#define HAS_ARM_SUPPORT
+#elif defined(__aarch64__)
+#define HAS_ARM64_SUPPORT
+#elif defined(__i386__) || defined(__x86_64__)
+ #define HAS_X86_SUPPORT
+#endif
+
+namespace google_breakpad {
+
+using std::string;
+
+// Use the REGISTER_FROM_THREADSTATE to access a register name from the
+// breakpad_thread_state_t structure.
+#if __DARWIN_OPAQUE_ARM_THREAD_STATE64
+#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) ((a)->__##b[i])
+#define GET_REGISTER_FROM_THREADSTATE_fp(a) \
+ (reinterpret_cast<uintptr_t>((a)->__opaque_fp))
+#define GET_REGISTER_FROM_THREADSTATE_lr(a) \
+ (reinterpret_cast<uintptr_t>((a)->__opaque_lr))
+#define GET_REGISTER_FROM_THREADSTATE_sp(a) \
+ (reinterpret_cast<uintptr_t>((a)->__opaque_sp))
+#define GET_REGISTER_FROM_THREADSTATE_pc(a) \
+ (reinterpret_cast<uintptr_t>((a)->__opaque_pc))
+#define GET_REGISTER_FROM_THREADSTATE_cpsr(a) ((a)->__cpsr)
+#define GET_REGISTER_FROM_THREADSTATE_flags(a) ((a)->__opaque_flags)
+#define REGISTER_FROM_THREADSTATE(a, b) (GET_REGISTER_FROM_THREADSTATE_##b(a))
+#elif __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM
+// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
+// i386_thread_state_t structure. There's no good way to tell what version of
+// the SDK we're compiling against so we just toggle on the same preprocessor
+// symbol Apple's headers use.
+#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b)
+#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) \
+ REGISTER_FROM_THREADSTATE(a, b[i])
+#else
+#define REGISTER_FROM_THREADSTATE(a, b) (a->b)
+#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) \
+ REGISTER_FROM_THREADSTATE(a, b[i])
+#endif
+
+// These typedefs only apply to the current process, whether or not it's the
+// one that crashed.
+#ifdef __LP64__
+typedef dyld_all_image_infos64 dyld_all_image_infos_self;
+#else
+typedef dyld_all_image_infos32 dyld_all_image_infos_self;
+#endif
+
+// Creates a minidump file of the current process. If there is exception data,
+// use SetExceptionInformation() to add this to the minidump. The minidump
+// file is generated by the Write() function.
+// Usage:
+// MinidumpGenerator minidump();
+// minidump.Write("/tmp/minidump");
+//
+class MinidumpGenerator {
+ public:
+ MinidumpGenerator();
+ MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
+
+ virtual ~MinidumpGenerator();
+
+ // Return <dir>/<unique_name>.dmp
+ // Sets |unique_name| (if requested) to the unique name for the minidump
+ static string UniqueNameInDirectory(const string &dir, string *unique_name);
+
+ // Write out the minidump into |path|
+ // All of the components of |path| must exist and be writable
+ // Return true if successful, false otherwise
+ bool Write(const char *path);
+
+ // Specify some exception information, if applicable
+ void SetExceptionInformation(int type, int64_t code, int64_t subcode,
+ mach_port_t thread_name) {
+ exception_type_ = type;
+ exception_code_ = code;
+ exception_subcode_ = subcode;
+ exception_thread_ = thread_name;
+ }
+
+ // Specify the task context. If |task_context| is not NULL, it will be used
+ // to retrieve the context of the current thread, instead of using
+ // |thread_get_state|.
+ void SetTaskContext(breakpad_ucontext_t *task_context);
+
+ // Gather system information. This should be call at least once before using
+ // the MinidumpGenerator class.
+ static void GatherSystemInformation();
+
+ // Get the slide for a module in the current process.
+ static uint64_t GetCurrentProcessModuleSlide(breakpad_mach_header* mh,
+ uint64_t shared_cache_slide);
+
+ // Gather information about the dyld module in the current process. This
+ // information is only relevant if the current process is also the crashing
+ // process.
+ void GatherCurrentProcessDyldInformation();
+
+ protected:
+ // Overridable Stream writers
+ virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
+
+ // Overridable Helper
+ virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
+
+ private:
+ typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
+
+ // Stream writers
+ bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
+ bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
+ bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
+ bool WriteModuleListStream(MDRawDirectory *module_list_stream);
+ bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
+ bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);
+ bool WriteCrashInfoStream(MDRawDirectory *crash_info_stream);
+ bool WriteBootargsStream(MDRawDirectory *bootargs_stream);
+ bool WriteThreadNamesStream(MDRawDirectory *thread_names_stream);
+
+ // Helpers
+ uint64_t CurrentPCForStack(breakpad_thread_state_data_t state);
+ bool GetThreadState(thread_act_t target_thread, thread_state_t state,
+ mach_msg_type_number_t *count);
+ bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
+ MDMemoryDescriptor *stack_location);
+ bool WriteStack(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContext(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ bool WriteCVRecord(MDRawModule *module, int cpu_type, int cpu_subtype,
+ const char *module_path, bool in_memory,
+ bool out_of_process, bool dyld_or_in_dyld_shared_cache);
+ bool WriteModuleStream(unsigned int index, MDRawModule *module);
+ bool WriteCrashInfoRecord(MDLocationDescriptor *location,
+ const char *module_path,
+ const char *crash_info,
+ unsigned long crash_info_size,
+ bool out_of_process,
+ bool dyld_or_in_dyld_shared_cache);
+ bool WriteThreadName(mach_port_t thread_id,
+ MDRawThreadName *thread_name);
+ size_t CalculateStackSize(mach_vm_address_t start_addr);
+ int FindExecutableModule();
+ bool IsValidExcCrash(uint64_t exception_code);
+ void RecoverExceptionDataFromExcCrash(uint64_t exception_code,
+ int& signal_number);
+
+ // Per-CPU implementations of these methods
+#ifdef HAS_ARM_SUPPORT
+ bool WriteStackARM(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextARM(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ bool WriteStackARM64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextARM64(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackARM64(breakpad_thread_state_data_t state);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ bool WriteStackPPC(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextPPC(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
+ bool WriteStackPPC64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextPPC64(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
+#endif
+#ifdef HAS_X86_SUPPORT
+ bool WriteStackX86(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextX86(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
+ bool WriteStackX86_64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location);
+ bool WriteContextX86_64(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location);
+ uint64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
+#endif
+
+ // disallow copy ctor and operator=
+ explicit MinidumpGenerator(const MinidumpGenerator &);
+ void operator=(const MinidumpGenerator &);
+
+ protected:
+ // Use this writer to put the data to disk
+ MinidumpFileWriter writer_;
+
+ private:
+ // Exception information
+ int exception_type_;
+ int64_t exception_code_;
+ int64_t exception_subcode_;
+ mach_port_t exception_thread_;
+ mach_port_t crashing_task_;
+ mach_port_t handler_thread_;
+
+ // CPU type of the task being dumped.
+ cpu_type_t cpu_type_;
+
+ // System information
+ static char build_string_[16];
+ static int os_major_version_;
+ static int os_minor_version_;
+ static int os_build_number_;
+
+ // Current process dyld information. It only applies to the crashed process
+ // if the current process is the one that crashed. It doesn't apply to the
+ // crashed process if the current process is the crash server and some other
+ // process has crashed.
+ breakpad_mach_header* dyldImageLoadAddress_;
+ ptrdiff_t dyldSlide_;
+ string dyldPath_;
+
+ // Context of the task to dump.
+ breakpad_ucontext_t *task_context_;
+
+ // Information about dynamically loaded code
+ DynamicImages *dynamic_images_;
+
+ // PageAllocator makes it possible to allocate memory
+ // directly from the system, even while handling an exception.
+ mutable PageAllocator allocator_;
+
+ protected:
+ // Blocks of memory written to the dump. These are all currently
+ // written while writing the thread list stream, but saved here
+ // so a memory list stream can be written afterwards.
+ wasteful_vector<MDMemoryDescriptor> memory_blocks_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_test.xcodeproj/project.pbxproj b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_test.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..1924ac4d58
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_test.xcodeproj/project.pbxproj
@@ -0,0 +1,843 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
+ 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
+ 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
+ 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
+ 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
+ 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
+ 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
+ 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
+ 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
+ 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; };
+ 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
+ 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
+ 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */; };
+ 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */; };
+ 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
+ 9BC1D2940B336F2300F2A2B4 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; };
+ 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
+ 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
+ 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */; };
+ 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */; };
+ 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
+ 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
+ 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
+ 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0A0B0133520055103E /* exception_handler.h */; };
+ 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
+ 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0C0B0133520055103E /* minidump_generator.h */; };
+ 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
+ 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
+ 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C240B01344C0055103E /* minidump_file_writer.h */; };
+ 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
+ 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
+ 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C2C0B01345E0055103E /* string_utilities.h */; };
+ D2F651000BEF947200920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
+ D2F651010BEF947200920385 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FB0BEF947200920385 /* file_id.h */; };
+ D2F651020BEF947200920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
+ D2F651030BEF947200920385 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FD0BEF947200920385 /* macho_id.h */; };
+ D2F651040BEF947200920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
+ D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FF0BEF947200920385 /* macho_utilities.h */; };
+ D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
+ D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F651080BEF949A00920385 /* dynamic_images.h */; };
+ D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
+ D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F6510D0BEF94EB00920385 /* macho_walker.h */; };
+ D2F651110BEF951700920385 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
+ D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF590B267D5F008DE8C7 /* string_conversion.h */; };
+ D2F651150BEF953000920385 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; };
+ D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */; };
+ D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
+ D2F6511D0BEF973500920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
+ D2F6511E0BEF973600920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
+ D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
+ D2F651210BEF975400920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
+ F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
+ F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
+ F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
+ F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
+ F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
+ F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.cc */; };
+ F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
+ F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
+ F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
+ F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; };
+ F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
+ F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
+ F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; };
+ F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
+ F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
+ F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
+ F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
+ F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8DD76F690486A84900D96B5E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */,
+ 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */,
+ 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */,
+ 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */,
+ 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */,
+ D2F651010BEF947200920385 /* file_id.h in CopyFiles */,
+ D2F651030BEF947200920385 /* macho_id.h in CopyFiles */,
+ D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */,
+ D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */,
+ D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */,
+ D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */,
+ D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
+ 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
+ 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
+ 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
+ 8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
+ 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; };
+ 8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = convert_UTF.cc; path = ../../../common/convert_UTF.cc; sourceTree = SOURCE_ROOT; };
+ 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
+ 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
+ 9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
+ 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
+ 9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler_test.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator_test.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82C090B0133520055103E /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82C0A0B0133520055103E /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = SOURCE_ROOT; };
+ 9BD82C0B0B0133520055103E /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82C0C0B0133520055103E /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = SOURCE_ROOT; };
+ 9BD82C230B01344C0055103E /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82C240B01344C0055103E /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../../minidump_file_writer.h; sourceTree = SOURCE_ROOT; };
+ 9BD82C2B0B01345E0055103E /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; };
+ 9BD82C2C0B01345E0055103E /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; };
+ 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; };
+ D2F650FA0BEF947200920385 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; };
+ D2F650FB0BEF947200920385 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; };
+ D2F650FC0BEF947200920385 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; };
+ D2F650FD0BEF947200920385 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; };
+ D2F650FE0BEF947200920385 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
+ D2F650FF0BEF947200920385 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
+ D2F651070BEF949A00920385 /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
+ D2F651080BEF949A00920385 /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
+ D2F6510C0BEF94EB00920385 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; };
+ D2F6510D0BEF94EB00920385 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; };
+ F917C4F70E03265A00F86017 /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = breakpad_exc_server.c; sourceTree = "<group>"; };
+ F917C4F80E03265A00F86017 /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_exc_server.h; sourceTree = "<group>"; };
+ F93A88750E8B4C700026AF89 /* octestcases.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = octestcases.octest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "obj-cTestCases-Info.plist"; sourceTree = "<group>"; };
+ F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
+ F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
+ F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
+ F9721F390E8B0D0D00D7E813 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = SOURCE_ROOT; };
+ F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
+ F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
+ F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
+ F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+ F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; };
+ F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
+ F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
+ F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
+ F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
+ F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests32-Info.plist"; sourceTree = "<group>"; };
+ F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests64.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests32.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests64-Info.plist"; sourceTree = "<group>"; };
+ F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicImagesTests.h; sourceTree = "<group>"; };
+ F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicImagesTests.cc; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DD76F660486A84900D96B5E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */,
+ 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 9B7CA84C0B1297F200CD3A1D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 9BD82A990B00267E0055103E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */,
+ 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93A88720E8B4C700026AF89 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
+ 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */,
+ 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE19C00DB04A9500C98454 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
+ 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE5B300DBFDBA300505983 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */,
+ F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */,
+ 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
+ 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
+ isa = PBXGroup;
+ children = (
+ 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
+ 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
+ 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
+ F9721FA80E8B0E4800D7E813 /* md5.cc */,
+ F9721F760E8B0DC700D7E813 /* bytereader.cc */,
+ F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
+ F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
+ F9721F390E8B0D0D00D7E813 /* dump_syms.cc */,
+ F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
+ F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
+ F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
+ F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */,
+ F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */,
+ D2F6510C0BEF94EB00920385 /* macho_walker.cc */,
+ D2F6510D0BEF94EB00920385 /* macho_walker.h */,
+ D2F651070BEF949A00920385 /* dynamic_images.cc */,
+ D2F651080BEF949A00920385 /* dynamic_images.h */,
+ D2F650FA0BEF947200920385 /* file_id.cc */,
+ D2F650FB0BEF947200920385 /* file_id.h */,
+ D2F650FC0BEF947200920385 /* macho_id.cc */,
+ D2F650FD0BEF947200920385 /* macho_id.h */,
+ D2F650FE0BEF947200920385 /* macho_utilities.cc */,
+ D2F650FF0BEF947200920385 /* macho_utilities.h */,
+ F9C5A41F0DB82DB000209C76 /* testcases */,
+ 9BD82C040B0133420055103E /* Breakpad */,
+ 08FB7795FE84155DC02AAC07 /* Source */,
+ 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */,
+ 1AB674ADFE9D54B511CA2CBB /* Products */,
+ F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */,
+ F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */,
+ F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */,
+ );
+ name = MinidumpWriter;
+ sourceTree = "<group>";
+ };
+ 08FB7795FE84155DC02AAC07 /* Source */ = {
+ isa = PBXGroup;
+ children = (
+ 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */,
+ 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */,
+ 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */,
+ );
+ name = Source;
+ sourceTree = "<group>";
+ };
+ 1AB674ADFE9D54B511CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DD76F6C0486A84900D96B5E /* generator_test */,
+ 9BD82A9B0B00267E0055103E /* handler_test */,
+ 9B7CA84E0B1297F200CD3A1D /* unit_test */,
+ F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */,
+ F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */,
+ F93A88750E8B4C700026AF89 /* octestcases.octest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */,
+ 8BFC815411FF9B7F002CB4DC /* Carbon.framework */,
+ F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */,
+ F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */,
+ 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */,
+ 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 9BD82C040B0133420055103E /* Breakpad */ = {
+ isa = PBXGroup;
+ children = (
+ 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */,
+ 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */,
+ 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */,
+ 9B35FF590B267D5F008DE8C7 /* string_conversion.h */,
+ 9BD82C090B0133520055103E /* exception_handler.cc */,
+ 9BD82C0A0B0133520055103E /* exception_handler.h */,
+ 9BD82C0B0B0133520055103E /* minidump_generator.cc */,
+ 9BD82C0C0B0133520055103E /* minidump_generator.h */,
+ 9BD82C230B01344C0055103E /* minidump_file_writer.cc */,
+ 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */,
+ 9BD82C240B01344C0055103E /* minidump_file_writer.h */,
+ 9BD82C2B0B01345E0055103E /* string_utilities.cc */,
+ 9BD82C2C0B01345E0055103E /* string_utilities.h */,
+ );
+ name = Breakpad;
+ sourceTree = "<group>";
+ };
+ F9C5A41F0DB82DB000209C76 /* testcases */ = {
+ isa = PBXGroup;
+ children = (
+ F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */,
+ F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */,
+ F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */,
+ F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */,
+ F9721F300E8B07E800D7E813 /* dwarftests.h */,
+ F9721F310E8B07E800D7E813 /* dwarftests.mm */,
+ );
+ path = testcases;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8DD76F620486A84900D96B5E /* generator_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */;
+ buildPhases = (
+ 8DD76F640486A84900D96B5E /* Sources */,
+ 8DD76F660486A84900D96B5E /* Frameworks */,
+ 8DD76F690486A84900D96B5E /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = generator_test;
+ productInstallPath = "$(HOME)/bin";
+ productName = MinidumpWriter;
+ productReference = 8DD76F6C0486A84900D96B5E /* generator_test */;
+ productType = "com.apple.product-type.tool";
+ };
+ 9B7CA84D0B1297F200CD3A1D /* unit_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */;
+ buildPhases = (
+ 9B7CA84B0B1297F200CD3A1D /* Sources */,
+ 9B7CA84C0B1297F200CD3A1D /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = unit_test;
+ productName = "filewriter unit test";
+ productReference = 9B7CA84E0B1297F200CD3A1D /* unit_test */;
+ productType = "com.apple.product-type.tool";
+ };
+ 9BD82A9A0B00267E0055103E /* handler_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */;
+ buildPhases = (
+ 9BD82A980B00267E0055103E /* Sources */,
+ 9BD82A990B00267E0055103E /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = handler_test;
+ productName = ExceptionTester;
+ productReference = 9BD82A9B0B00267E0055103E /* handler_test */;
+ productType = "com.apple.product-type.tool";
+ };
+ F93A88740E8B4C700026AF89 /* obj-c_TestCases */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */;
+ buildPhases = (
+ F93A88700E8B4C700026AF89 /* Resources */,
+ F93A88710E8B4C700026AF89 /* Sources */,
+ F93A88720E8B4C700026AF89 /* Frameworks */,
+ F93A88730E8B4C700026AF89 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "obj-c_TestCases";
+ productName = octestcases;
+ productReference = F93A88750E8B4C700026AF89 /* octestcases.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ F9AE19C20DB04A9500C98454 /* minidump_tests64 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */;
+ buildPhases = (
+ F9AE19BE0DB04A9500C98454 /* Resources */,
+ F9AE19BF0DB04A9500C98454 /* Sources */,
+ F9AE19C00DB04A9500C98454 /* Frameworks */,
+ F9AE19C10DB04A9500C98454 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = minidump_tests64;
+ productName = minidump_tests;
+ productReference = F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ F9AE5B320DBFDBA300505983 /* minidump_tests32 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */;
+ buildPhases = (
+ F9AE5B2E0DBFDBA300505983 /* Resources */,
+ F9AE5B2F0DBFDBA300505983 /* Sources */,
+ F9AE5B300DBFDBA300505983 /* Frameworks */,
+ F9AE5B310DBFDBA300505983 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = minidump_tests32;
+ productName = Untitled;
+ productReference = F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */;
+ productType = "com.apple.product-type.bundle";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
+ mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8DD76F620486A84900D96B5E /* generator_test */,
+ 9BD82A9A0B00267E0055103E /* handler_test */,
+ 9B7CA84D0B1297F200CD3A1D /* unit_test */,
+ F9AE19C20DB04A9500C98454 /* minidump_tests64 */,
+ F9AE5B320DBFDBA300505983 /* minidump_tests32 */,
+ F93A88740E8B4C700026AF89 /* obj-c_TestCases */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ F93A88700E8B4C700026AF89 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE19BE0DB04A9500C98454 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE5B2E0DBFDBA300505983 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ F93A88730E8B4C700026AF89 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
+ };
+ F9AE19C10DB04A9500C98454 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n# Run gcov on the framework getting tested\nif [ \"${CONFIGURATION}\" = 'Coverage' ];\nthen\n FRAMEWORK_NAME=minidump_tests64\n FRAMEWORK_OBJ_DIR=${OBJROOT}/${PROJECT_NAME}.build/${CONFIGURATION}/${FRAMEWORK_NAME}.build/Objects-normal/${NATIVE_ARCH_ACTUAL}\n mkdir -p coverage\n pushd coverage\n echo find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n popd\nfi ";
+ };
+ F9AE5B310DBFDBA300505983 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DD76F640486A84900D96B5E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */,
+ 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */,
+ 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */,
+ 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */,
+ 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */,
+ D2F651000BEF947200920385 /* file_id.cc in Sources */,
+ D2F651020BEF947200920385 /* macho_id.cc in Sources */,
+ D2F651040BEF947200920385 /* macho_utilities.cc in Sources */,
+ D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */,
+ D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */,
+ D2F651110BEF951700920385 /* string_conversion.cc in Sources */,
+ D2F651150BEF953000920385 /* convert_UTF.cc in Sources */,
+ 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 9B7CA84B0B1297F200CD3A1D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */,
+ 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */,
+ 9BC1D2940B336F2300F2A2B4 /* convert_UTF.cc in Sources */,
+ 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */,
+ 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 9BD82A980B00267E0055103E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */,
+ 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */,
+ 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */,
+ 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */,
+ 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */,
+ 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.cc in Sources */,
+ 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */,
+ D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */,
+ D2F6511D0BEF973500920385 /* file_id.cc in Sources */,
+ D2F6511E0BEF973600920385 /* macho_id.cc in Sources */,
+ D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */,
+ D2F651210BEF975400920385 /* macho_walker.cc in Sources */,
+ 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93A88710E8B4C700026AF89 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
+ F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */,
+ F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
+ F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
+ F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
+ F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */,
+ F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
+ F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
+ F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */,
+ F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE19BF0DB04A9500C98454 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */,
+ F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */,
+ F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */,
+ F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AE5B2F0DBFDBA300505983 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
+ F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
+ 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB923208733DC60010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++17";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
+ );
+ PRODUCT_NAME = generator_test;
+ USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
+ };
+ name = Debug;
+ };
+ 1DEB923308733DC60010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++17";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
+ );
+ PRODUCT_NAME = generator_test;
+ USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
+ };
+ name = Release;
+ };
+ 1DEB923608733DC60010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 1DEB923708733DC60010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ 9B7CA8510B12984300CD3A1D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = unit_test;
+ USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
+ };
+ name = Debug;
+ };
+ 9B7CA8520B12984300CD3A1D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = unit_test;
+ USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
+ };
+ name = Release;
+ };
+ 9BD82AA70B0026BF0055103E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = handler_test;
+ USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
+ };
+ name = Debug;
+ };
+ 9BD82AA80B0026BF0055103E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = handler_test;
+ USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
+ };
+ name = Release;
+ };
+ F93A88770E8B4C700026AF89 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "obj-cTestCases-Info.plist";
+ PRODUCT_NAME = octestcases;
+ USER_HEADER_SEARCH_PATHS = "../../../..//**";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ F93A88780E8B4C700026AF89 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "obj-cTestCases-Info.plist";
+ PRODUCT_NAME = octestcases;
+ USER_HEADER_SEARCH_PATHS = "../../../..//**";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+ F9AE19C40DB04A9500C98454 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "minidump_tests64-Info.plist";
+ PRODUCT_NAME = minidump_tests64;
+ USER_HEADER_SEARCH_PATHS = "../../../**";
+ WRAPPER_EXTENSION = cptest;
+ };
+ name = Debug;
+ };
+ F9AE19C50DB04A9500C98454 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "minidump_tests64-Info.plist";
+ PRODUCT_NAME = minidump_tests64;
+ USER_HEADER_SEARCH_PATHS = "../../../**";
+ WRAPPER_EXTENSION = cptest;
+ };
+ name = Release;
+ };
+ F9AE5B350DBFDBA300505983 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "minidump_tests32-Info.plist";
+ PRODUCT_NAME = minidump_tests32;
+ USER_HEADER_SEARCH_PATHS = "../../../**";
+ WRAPPER_EXTENSION = cptest;
+ };
+ name = Debug;
+ };
+ F9AE5B370DBFDBA300505983 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ INFOPLIST_FILE = "minidump_tests32-Info.plist";
+ PRODUCT_NAME = minidump_tests32;
+ USER_HEADER_SEARCH_PATHS = "../../../**";
+ WRAPPER_EXTENSION = cptest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB923208733DC60010E9CD /* Debug */,
+ 1DEB923308733DC60010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB923608733DC60010E9CD /* Debug */,
+ 1DEB923708733DC60010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 9B7CA8510B12984300CD3A1D /* Debug */,
+ 9B7CA8520B12984300CD3A1D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 9BD82AA70B0026BF0055103E /* Debug */,
+ 9BD82AA80B0026BF0055103E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F93A88770E8B4C700026AF89 /* Debug */,
+ F93A88780E8B4C700026AF89 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9AE19C40DB04A9500C98454 /* Debug */,
+ F9AE19C50DB04A9500C98454 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9AE5B350DBFDBA300505983 /* Debug */,
+ F9AE5B370DBFDBA300505983 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/moz.build b/toolkit/crashreporter/breakpad-client/mac/handler/moz.build
new file mode 100644
index 0000000000..7d123a2ef1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/moz.build
@@ -0,0 +1,22 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'breakpad_nlist_64.cc',
+ 'dynamic_images.cc',
+ 'exception_handler.cc',
+ 'minidump_generator.cc',
+]
+
+FINAL_LIBRARY = 'breakpad_client'
+
+LOCAL_INCLUDES += [
+ '/toolkit/crashreporter/breakpad-client',
+ '/toolkit/crashreporter/google-breakpad/src',
+]
+
+if CONFIG['MOZ_PHC']:
+ DEFINES['MOZ_PHC'] = True
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.cc b/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.cc
new file mode 100644
index 0000000000..6142ad124a
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.cc
@@ -0,0 +1,92 @@
+// 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.
+//
+// ProtectedMemoryAllocator
+//
+// See the header file for documentation
+
+#include "protected_memory_allocator.h"
+#include <assert.h>
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ProtectedMemoryAllocator::ProtectedMemoryAllocator(vm_size_t pool_size)
+ : pool_size_(pool_size),
+ next_alloc_offset_(0),
+ valid_(false) {
+
+ kern_return_t result = vm_allocate(mach_task_self(),
+ &base_address_,
+ pool_size,
+ TRUE
+ );
+
+ valid_ = (result == KERN_SUCCESS);
+ assert(valid_);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ProtectedMemoryAllocator::~ProtectedMemoryAllocator() {
+ vm_deallocate(mach_task_self(),
+ base_address_,
+ pool_size_
+ );
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) {
+ if (valid_ && next_alloc_offset_ + bytes <= pool_size_) {
+ char *p = (char*)base_address_ + next_alloc_offset_;
+ next_alloc_offset_ += bytes;
+ return p;
+ }
+
+ return NULL; // ran out of memory in our allocation block
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+kern_return_t ProtectedMemoryAllocator::Protect() {
+ kern_return_t result = vm_protect(mach_task_self(),
+ base_address_,
+ pool_size_,
+ FALSE,
+ VM_PROT_READ);
+
+ return result;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+kern_return_t ProtectedMemoryAllocator::Unprotect() {
+ kern_return_t result = vm_protect(mach_task_self(),
+ base_address_,
+ pool_size_,
+ FALSE,
+ VM_PROT_READ | VM_PROT_WRITE);
+
+ return result;
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.h b/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.h
new file mode 100644
index 0000000000..64ac23c00b
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/protected_memory_allocator.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.
+//
+// ProtectedMemoryAllocator
+//
+// A very simple allocator class which allows allocation, but not deallocation.
+// The allocations can be made read-only with the Protect() method.
+// This class is NOT useful as a general-purpose memory allocation system,
+// since it does not allow deallocation. It is useful to use for a group
+// of allocations which are created in the same time-frame and destroyed
+// in the same time-frame. It is useful for making allocations of memory
+// which will not need to change often once initialized. This memory can then
+// be protected from memory smashers by calling the Protect() method.
+
+#ifndef PROTECTED_MEMORY_ALLOCATOR_H__
+#define PROTECTED_MEMORY_ALLOCATOR_H__
+
+#include <mach/mach.h>
+
+//
+class ProtectedMemoryAllocator {
+ public:
+ ProtectedMemoryAllocator(vm_size_t pool_size);
+ ~ProtectedMemoryAllocator();
+
+ // Returns a pointer to an allocation of size n within the pool.
+ // Fails by returning NULL is no more space is available.
+ // Please note that the pointers returned from this method should not
+ // be freed in any way (for example by calling free() on them ).
+ char * Allocate(vm_size_t n);
+
+ // Returns the base address of the allocation pool.
+ char * GetBaseAddress() { return (char*)base_address_; }
+
+ // Returns the size of the allocation pool, including allocated
+ // plus free space.
+ vm_size_t GetTotalSize() { return pool_size_; }
+
+ // Returns the number of bytes already allocated in the pool.
+ vm_size_t GetAllocatedSize() { return next_alloc_offset_; }
+
+ // Returns the number of bytes available for allocation.
+ vm_size_t GetFreeSize() { return pool_size_ - next_alloc_offset_; }
+
+ // Makes the entire allocation pool read-only including, of course,
+ // all allocations made from the pool.
+ kern_return_t Protect();
+
+ // Makes the entire allocation pool read/write.
+ kern_return_t Unprotect();
+
+ private:
+ vm_size_t pool_size_;
+ vm_address_t base_address_;
+ vm_size_t next_alloc_offset_;
+ bool valid_;
+};
+
+#endif // PROTECTED_MEMORY_ALLOCATOR_H__
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.cc b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.cc
new file mode 100644
index 0000000000..42344ec5d3
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2008, 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.
+
+//
+// DynamicImagesTests.cpp
+// minidump_test
+//
+// Created by Neal Sidhwaney on 4/17/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+
+#include "mac/handler/testcases/DynamicImagesTests.h"
+#include "mac/handler/dynamic_images.h"
+
+DynamicImagesTests test2(TEST_INVOCATION(DynamicImagesTests,
+ ReadTaskMemoryTest));
+DynamicImagesTests test3(TEST_INVOCATION(DynamicImagesTests,
+ ReadLibrariesFromLocalTaskTest));
+
+DynamicImagesTests::DynamicImagesTests(TestInvocation *invocation)
+ : TestCase(invocation) {
+}
+
+DynamicImagesTests::~DynamicImagesTests() {
+}
+
+void DynamicImagesTests::ReadTaskMemoryTest() {
+ kern_return_t kr;
+
+ // pick test2 as a symbol we know to be valid to read
+ // anything will work, really
+ void *addr = reinterpret_cast<void*>(&test2);
+ std::vector<uint8_t> buf(getpagesize());
+
+ fprintf(stderr, "reading 0x%p\n", addr);
+ kr = google_breakpad::ReadTaskMemory(mach_task_self(),
+ (uint64_t)addr,
+ getpagesize(),
+ buf);
+
+ CPTAssert(kr == KERN_SUCCESS);
+
+ CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize()));
+}
+
+void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
+
+ mach_port_t me = mach_task_self();
+ google_breakpad::DynamicImages *d = new google_breakpad::DynamicImages(me);
+
+ fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
+
+ CPTAssert(d->GetImageCount() > 0);
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.h b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.h
new file mode 100644
index 0000000000..e1e79993bb
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/DynamicImagesTests.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2008, 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.
+
+//
+// DynamicImagesTests.h
+// minidump_test
+//
+// Created by Neal Sidhwaney on 4/17/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+//
+
+#ifndef _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__
+#define _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__
+
+#include <CPlusTest/CPlusTest.h>
+
+class DynamicImagesTests : public TestCase {
+ public:
+ explicit DynamicImagesTests(TestInvocation* invocation);
+ virtual ~DynamicImagesTests();
+
+ void ReadTaskMemoryTest();
+ void ReadLibrariesFromLocalTaskTest();
+};
+
+#endif /* _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ */
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.cc b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.cc
new file mode 100644
index 0000000000..ee3248116d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2008, 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.
+
+//
+// breakpad_nlist_test.cc
+// minidump_test
+//
+// Created by Neal Sidhwaney on 4/13/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+
+#include "mac/handler/testcases/breakpad_nlist_test.h"
+#include <mach-o/nlist.h>
+#include "mac/handler/breakpad_nlist_64.h"
+
+BreakpadNlistTest test1(TEST_INVOCATION(BreakpadNlistTest, CompareToNM));
+
+BreakpadNlistTest::BreakpadNlistTest(TestInvocation *invocation)
+ : TestCase(invocation) {
+}
+
+
+BreakpadNlistTest::~BreakpadNlistTest() {
+}
+
+void BreakpadNlistTest::CompareToNM() {
+#if TARGET_CPU_X86_64
+ system("/usr/bin/nm -arch x86_64 /usr/lib/dyld > /tmp/dyld-namelist.txt");
+#elif TARGET_CPU_PPC64
+ system("/usr/bin/nm -arch ppc64 /usr/lib/dyld > /tmp/dyld-namelist.txt");
+#endif
+
+ FILE *fd = fopen("/tmp/dyld-namelist.txt", "rt");
+
+ char oneNMAddr[30];
+ char symbolType;
+ char symbolName[500];
+ while (!feof(fd)) {
+ fscanf(fd, "%s %c %s", oneNMAddr, &symbolType, symbolName);
+ breakpad_nlist symbolList[2];
+ breakpad_nlist &list = symbolList[0];
+
+ memset(symbolList, 0, sizeof(breakpad_nlist)*2);
+ const char *symbolNames[2];
+ symbolNames[0] = (const char*)symbolName;
+ symbolNames[1] = "\0";
+ breakpad_nlist_64("/usr/lib/dyld", &list, symbolNames);
+ uint64_t nmAddr = strtol(oneNMAddr, NULL, 16);
+ if (!IsSymbolMoreThanOnceInDyld(symbolName)) {
+ CPTAssert(nmAddr == symbolList[0].n_value);
+ }
+ }
+
+ fclose(fd);
+}
+
+bool BreakpadNlistTest::IsSymbolMoreThanOnceInDyld(const char *symbolName) {
+ // These are the symbols that occur more than once when nm dumps
+ // the symbol table of /usr/lib/dyld. Our nlist program returns
+ // the first address because it's doing a search so we need to exclude
+ // these from causing the test to fail
+ const char *multipleSymbols[] = {
+ "__Z41__static_initialization_and_destruction_0ii",
+ "___tcf_0",
+ "___tcf_1",
+ "_read_encoded_value_with_base",
+ "_read_sleb128",
+ "_read_uleb128",
+ "\0"};
+
+ bool found = false;
+
+ for (int i = 0; multipleSymbols[i][0]; i++) {
+ if (!strcmp(multipleSymbols[i], symbolName)) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.h b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.h
new file mode 100644
index 0000000000..e93657cc90
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/breakpad_nlist_test.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2008, 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.
+
+//
+// breakpad_nlist_test.h
+// minidump_test
+//
+// Created by Neal Sidhwaney on 4/13/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+//
+
+#ifndef CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__
+#define CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__
+
+#include <CPlusTest/CPlusTest.h>
+
+class BreakpadNlistTest : public TestCase {
+ private:
+
+ // nm dumps multiple addresses for the same symbol in
+ // /usr/lib/dyld. So we track those so we don't report failures
+ // in mismatches between what our nlist returns and what nm has
+ // for the duplicate symbols.
+ bool IsSymbolMoreThanOnceInDyld(const char *symbolName);
+
+ public:
+ explicit BreakpadNlistTest(TestInvocation* invocation);
+ virtual ~BreakpadNlistTest();
+
+
+ /* This test case runs nm on /usr/lib/dyld and then compares the
+ output of every symbol to what our nlist implementation returns */
+ void CompareToNM();
+};
+
+#endif /* CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__*/
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.h b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.h
new file mode 100644
index 0000000000..21ff7a44f0
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2008, 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.
+
+//
+// dwarftests.h
+// minidump_test
+//
+// Created by Neal Sidhwaney on 9/24/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+
+
+@interface dwarftests : SenTestCase {
+
+}
+
+- (void) testDWARFSymbolFileGeneration;
+
+@end
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.mm b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.mm
new file mode 100644
index 0000000000..40c69aff25
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/dwarftests.mm
@@ -0,0 +1,60 @@
+// Copyright (c) 2008, 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.
+
+//
+// dwarftests.m
+// minidump_test
+//
+// Created by Neal Sidhwaney on 9/24/08.
+// Copyright 2008 Google Inc. All rights reserved.
+//
+
+#import "dwarftests.h"
+#import "dump_syms.h"
+
+@implementation dwarftests
+- (void) testDWARFSymbolFileGeneration {
+ NSString *inputBreakpadSymbolFile = @"testcases/testdata/dump_syms_i386_breakpad.sym";
+ NSString *outputBreakpadSymbolFile = @"/tmp/dump_syms_i386.breakpad";
+
+ DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:@"testcases/testdata/dump_syms_dwarf_data"];
+
+ STAssertNotNil(dump, @"DumpSymbols is nil");
+ [dump setArchitecture:@"i386"];
+ [dump writeSymbolFile:outputBreakpadSymbolFile];
+
+ NSData *d = [[NSData alloc] initWithContentsOfFile:inputBreakpadSymbolFile];
+ STAssertNotNil(d, @"Input breakpad symbol file not found");
+
+ NSData *d1 = [[NSData alloc] initWithContentsOfFile:outputBreakpadSymbolFile];
+ STAssertNotNil(d1, @"Output breakpad symbol file not found");
+
+ STAssertTrue([d isEqualToData:d1],
+ @"Symbol files were not equal!",nil);
+}
+@end
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_dwarf_data b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_dwarf_data
new file mode 100644
index 0000000000..5be17aeedc
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_dwarf_data
Binary files differ
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym
new file mode 100644
index 0000000000..bca43c1037
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym
@@ -0,0 +1,5300 @@
+MODULE mac x86 94BF873C47A73BC07125291390B4C5F10 dump_syms_dwarf_data
+FILE 1 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/libkern/i386/OSByteOrder.h
+FILE 2 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/architecture/byte_order.h
+FILE 3 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/dump_syms.mm
+FILE 4 /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSRange.h
+FILE 5 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/new
+FILE 6 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/hash_fun.h
+FILE 7 ../../../common/mac/dwarf/dwarf2reader.h
+FILE 8 ../../../common/mac/file_id.h
+FILE 9 ../../../common/mac/dwarf/functioninfo.h
+FILE 10 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_tree.h
+FILE 11 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_iterator.h
+FILE 12 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/hashtable.h
+FILE 13 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_pair.h
+FILE 14 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/new_allocator.h
+FILE 15 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/allocator.h
+FILE 16 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_vector.h
+FILE 17 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_iterator_base_types.h
+FILE 18 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_iterator_base_funcs.h
+FILE 19 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_algo.h
+FILE 20 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_map.h
+FILE 21 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_construct.h
+FILE 22 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_function.h
+FILE 23 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/hash_map
+FILE 24 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/basic_string.h
+FILE 25 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_algobase.h
+FILE 26 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_list.h
+FILE 27 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/list.tcc
+FILE 28 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_uninitialized.h
+FILE 29 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/vector.tcc
+FILE 30 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/dwarf/functioninfo.cc
+FILE 31 ../../../common/mac/dwarf/dwarf2reader.h
+FILE 32 ../../../common/mac/dwarf/functioninfo.h
+FILE 33 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_pair.h
+FILE 34 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/hashtable.h
+FILE 35 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/memory
+FILE 36 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/new_allocator.h
+FILE 37 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/basic_string.h
+FILE 38 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_iterator.h
+FILE 39 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_vector.h
+FILE 40 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_tree.h
+FILE 41 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_function.h
+FILE 42 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/hash_map
+FILE 43 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_construct.h
+FILE 44 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_algobase.h
+FILE 45 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_map.h
+FILE 46 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_uninitialized.h
+FILE 47 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/vector.tcc
+FILE 48 /g/code/breakpad-staging/src/tools/mac/dump_syms/dump_syms_tool.mm
+FILE 49 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/file_id.cc
+FILE 50 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/architecture/byte_order.h
+FILE 51 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/macho_id.cc
+FILE 52 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/libkern/i386/OSByteOrder.h
+FILE 53 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/architecture/byte_order.h
+FILE 54 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/macho_walker.cc
+FILE 55 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/libkern/i386/OSByteOrder.h
+FILE 56 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/architecture/byte_order.h
+FILE 57 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/macho_utilities.cc
+FILE 58 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/dwarf/bytereader.cc
+FILE 59 ../../../common/mac/dwarf/bytereader-inl.h
+FILE 60 /g/code/breakpad-staging/src/tools/mac/dump_syms/../../../common/mac/dwarf/dwarf2reader.cc
+FILE 61 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_algobase.h
+FILE 62 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_deque.h
+FILE 63 ../../../common/mac/dwarf/bytereader.h
+FILE 64 ../../../common/mac/dwarf/bytereader-inl.h
+FILE 65 ../../../common/mac/dwarf/line_state_machine.h
+FILE 66 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_list.h
+FILE 67 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/memory
+FILE 68 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/ext/new_allocator.h
+FILE 69 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/allocator.h
+FILE 70 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_pair.h
+FILE 71 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_vector.h
+FILE 72 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_iterator.h
+FILE 73 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_construct.h
+FILE 74 ../../../common/mac/dwarf/dwarf2reader.h
+FILE 75 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_stack.h
+FILE 76 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/deque.tcc
+FILE 77 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/list.tcc
+FILE 78 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/vector.tcc
+FILE 79 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_uninitialized.h
+FILE 80 /var/tmp/gcc/gcc-5484~1/src/gcc/libgcc2.c
+FUNC 162a 28 0 _OSSwapInt16
+162a 10 44 55
+163a 16 46 55
+1650 2 47 55
+FUNC 1652 1c 0 _OSSwapInt32
+1652 f 53 55
+1661 8 55 55
+1669 3 56 55
+166c 2 57 55
+FUNC 166e 2b 0 _OSSwapInt64
+166e 12 64 55
+1680 11 69 55
+1691 6 70 55
+1697 2 71 55
+1699 1 71 55
+FUNC 169a 1e 0 NXSwapShort
+169a 10 43 56
+16aa c 45 56
+16b6 2 46 56
+FUNC 16b8 19 0 NXSwapInt
+16b8 f 52 56
+16c7 8 54 56
+16cf 2 55 56
+16d1 1 55 56
+FUNC 16d2 19 0 NXSwapLong
+16d2 f 61 56
+16e1 8 63 56
+16e9 2 64 56
+16eb 1 64 56
+FUNC 16ec 1f 0 NXSwapLongLong
+16ec 12 70 56
+16fe b 72 56
+1709 2 73 56
+170b 1 73 56
+FUNC 170c 181 0 -[DumpSymbols convertCPlusPlusSymbols:]
+170c 14 128 3
+1720 54 130 3
+1774 f 132 3
+1783 7 133 3
+178a 1a 136 3
+17a4 5 138 3
+17a9 1a 139 3
+17c3 23 140 3
+17e6 7 141 3
+17ed 44 142 3
+1831 1e 145 3
+184f 29 138 3
+1878 b 148 3
+1883 3 150 3
+1886 7 151 3
+188d 1 151 3
+FUNC 188e 323 0 -[DumpSymbols convertSymbols]
+188e 14 154 3
+18a2 1f 155 3
+18c1 3e 156 3
+18ff 2b 160 3
+192a c 162 3
+1936 43 164 3
+1979 2a 165 3
+19a3 20 168 3
+19c3 d 169 3
+19d0 1e 171 3
+19ee 11 162 3
+19ff 7 181 3
+1a06 6 182 3
+1a0c 5 184 3
+1a11 15 185 3
+1a26 6 18 4
+1a2c 6 19 4
+1a32 6 20 4
+1a38 6 185 3
+1a3e 28 186 3
+1a66 21 187 3
+1a87 1a 188 3
+1aa1 a 190 3
+1aab c 194 3
+1ab7 43 198 3
+1afa 21 199 3
+1b1b 20 202 3
+1b3b 2e 203 3
+1b69 1e 194 3
+1b87 c 184 3
+1b93 17 207 3
+1baa 7 208 3
+1bb1 1 208 3
+FUNC 1bb2 4a2 0 -[DumpSymbols addFunction:line:address:section:]
+1bb2 21 211 3
+1bd3 2f 212 3
+1c02 e 214 3
+1c10 4 219 3
+1c14 2a 221 3
+1c3e 22 223 3
+1c60 6 224 3
+1c66 2a 225 3
+1c90 4 226 3
+1c94 2e 230 3
+1cc2 2e 233 3
+1cf0 2e 236 3
+1d1e a 239 3
+1d28 2b 253 3
+1d53 e 254 3
+1d61 3c 255 3
+1d9d 22 32 4
+1dbf 3 256 3
+1dc2 6 259 3
+1dc8 a 260 3
+1dd2 3c 261 3
+1e0e 25 262 3
+1e33 2a 263 3
+1e5d 22 265 3
+1e7f 26 270 3
+1ea5 6 272 3
+1eab 37 273 3
+1ee2 2a 274 3
+1f0c 17 275 3
+1f23 43 278 3
+1f66 2e 279 3
+1f94 23 282 3
+1fb7 43 285 3
+1ffa 52 287 3
+204c 8 289 3
+FUNC 2054 5a4 0 -[DumpSymbols processSymbolItem:stringTable:]
+2054 18 292 3
+206c 8 293 3
+2074 4 294 3
+2078 16 297 3
+208e c 298 3
+209a f 300 3
+20a9 b 301 3
+20b4 16 303 3
+20ca 4d 309 3
+2117 38 311 3
+214f 30 315 3
+217f 60 317 3
+21df d 322 3
+21ec 2b 325 3
+2217 3a 327 3
+2251 f 332 3
+2260 2d 333 3
+228d 1a 334 3
+22a7 32 335 3
+22d9 20 342 3
+22f9 c 343 3
+2305 24 348 3
+2329 a 349 3
+2333 3c 350 3
+236f 2a 352 3
+2399 1c 353 3
+23b5 9 354 3
+23be f 356 3
+23cd 2d 357 3
+23fa 2f 358 3
+2429 20 360 3
+2449 c 361 3
+2455 7 363 3
+245c 21 365 3
+247d 4a 368 3
+24c7 9 370 3
+24d0 1a 371 3
+24ea 4b 372 3
+2535 4 373 3
+2539 5 371 3
+253e 29 374 3
+2567 2d 376 3
+2594 4b 378 3
+25df 4 379 3
+25e3 a 382 3
+25ed b 383 3
+FUNC 25f8 c9 0 -[DumpSymbols loadSymbolInfo:offset:]
+25f8 13 391 3
+260b 2b 392 3
+2636 2a 393 3
+2660 2d 395 3
+268d 2e 398 3
+26bb 6 399 3
+26c1 1 399 3
+FUNC 26c2 2be 0 -[DumpSymbols loadSTABSSymbolInfo:offset:]
+26c2 16 537 3
+26d8 9 538 3
+26e1 10 539 3
+26f1 2e 540 3
+271f 9 542 3
+2728 22 543 3
+274a 4 544 3
+274e a 546 3
+2758 3c 547 3
+2794 c 549 3
+27a0 e 550 3
+27ae 6 551 3
+27b4 25 552 3
+27d9 25 553 3
+27fe 25 554 3
+2823 c 555 3
+282f c 556 3
+283b c 559 3
+2847 23 562 3
+286a a 563 3
+2874 a 564 3
+287e 2e 565 3
+28ac 39 566 3
+28e5 2e 570 3
+2913 4 571 3
+2917 17 559 3
+292e 25 575 3
+2953 9 576 3
+295c 17 549 3
+2973 4 579 3
+2977 9 580 3
+FUNC 2980 28a 0 -[DumpSymbols loadSymbolInfo64:offset:]
+2980 16 583 3
+2996 9 585 3
+299f 10 586 3
+29af 2e 587 3
+29dd 9 589 3
+29e6 22 590 3
+2a08 4 591 3
+2a0c c 593 3
+2a18 e 594 3
+2a26 6 595 3
+2a2c 25 596 3
+2a51 25 597 3
+2a76 25 598 3
+2a9b 9 599 3
+2aa4 c 600 3
+2ab0 c 603 3
+2abc 17 604 3
+2ad3 23 609 3
+2af6 a 610 3
+2b00 a 611 3
+2b0a 2e 612 3
+2b38 37 613 3
+2b6f 2e 615 3
+2b9d 4 616 3
+2ba1 17 603 3
+2bb8 25 620 3
+2bdd 9 621 3
+2be6 17 593 3
+2bfd 4 624 3
+2c01 9 625 3
+FUNC 2c0a 199 0 -[DumpSymbols loadSymbolInfoForArchitecture]
+2c0a 13 628 3
+2c1d 41 630 3
+2c5e 2b 631 3
+2c89 1a 632 3
+2ca3 40 634 3
+2ce3 40 635 3
+2d23 5f 637 3
+2d82 17 639 3
+2d99 4 640 3
+2d9d 6 641 3
+2da3 1 641 3
+FUNC 2da4 3e5 0 -[DumpSymbols loadHeader:offset:]
+2da4 18 728 3
+2dbc 9 729 3
+2dc5 10 730 3
+2dd5 2e 731 3
+2e03 9 733 3
+2e0c 2b 734 3
+2e37 1e 736 3
+2e55 c 738 3
+2e61 e 739 3
+2e6f 6 740 3
+2e75 50 742 3
+2ec5 2e 743 3
+2ef3 2e 744 3
+2f21 2e 745 3
+2f4f 20 746 3
+2f6f 1b7 755 3
+3126 9 757 3
+312f 25 761 3
+3154 9 762 3
+315d 17 738 3
+3174 a 765 3
+317e b 766 3
+3189 1 766 3
+FUNC 318a 41d 0 -[DumpSymbols loadHeader64:offset:]
+318a 18 769 3
+31a2 9 771 3
+31ab 10 772 3
+31bb 2e 773 3
+31e9 9 775 3
+31f2 c 777 3
+31fe 2b 778 3
+3229 e 779 3
+3237 6 780 3
+323d 50 781 3
+328d 49 782 3
+32d6 49 783 3
+331f 2e 784 3
+334d 9 785 3
+3356 29 786 3
+337f 1c5 794 3
+3544 9 795 3
+354d 25 799 3
+3572 9 800 3
+357b 17 777 3
+3592 a 803 3
+359c b 804 3
+35a7 1 804 3
+FUNC 35a8 52a 0 -[DumpSymbols loadModuleInfo]
+35a8 14 807 3
+35bc e 808 3
+35ca 41 810 3
+360b 1a 811 3
+3625 6 812 3
+362b 6 814 3
+3631 17 815 3
+3648 c 816 3
+3654 29 820 3
+367d 29 821 3
+36a6 29 822 3
+36cf 35 824 3
+3704 12 826 3
+3716 17 827 3
+372d c 828 3
+3739 3c 832 3
+3775 a 834 3
+377f 9 836 3
+3788 25 837 3
+37ad c 839 3
+37b9 54 840 3
+380d 57 841 3
+3864 57 842 3
+38bb 57 843 3
+3912 57 844 3
+3969 1c 846 3
+3985 4b 847 3
+39d0 49 849 3
+3a19 13 839 3
+3a2c 6 851 3
+3a32 3c 852 3
+3a6e 3a 854 3
+3aa8 17 857 3
+3abf c 858 3
+3acb 7 859 3
+FUNC 3ad2 b6 0 WriteFormat
+3ad2 10 862 3
+3ae2 6 867 3
+3ae8 24 868 3
+3b0c 27 869 3
+3b33 40 870 3
+3b73 c 873 3
+3b7f 9 874 3
+FUNC 3b88 35 0 -[DumpSymbols availableArchitectures]
+3b88 13 1140 3
+3b9b 1c 1141 3
+3bb7 6 1142 3
+3bbd 1 1142 3
+FUNC 3bbe 1b4 0 -[DumpSymbols setArchitecture:]
+3bbe 13 1158 3
+3bd1 1a 1159 3
+3beb 4 1160 3
+3bef 2a 1162 3
+3c19 9 1163 3
+3c22 2a 1165 3
+3c4c 9 1166 3
+3c55 9 1167 3
+3c5e 2a 1169 3
+3c88 6 1170 3
+3c8e 2a 1172 3
+3cb8 6 1173 3
+3cbe 2a 1175 3
+3ce8 4 1176 3
+3cec 6 1179 3
+3cf2 2c 1180 3
+3d1e 9 1181 3
+3d27 1c 1183 3
+3d43 1f 1184 3
+3d62 a 1187 3
+3d6c 6 1188 3
+FUNC 3d72 14 0 -[DumpSymbols architecture]
+3d72 c 1191 3
+3d7e 6 1192 3
+3d84 2 1193 3
+FUNC 3d86 e7 0 -[DumpSymbols writeSymbolFile:]
+3d86 13 1196 3
+3d99 1a 1197 3
+3db3 48 1200 3
+3dfb 9 1201 3
+3e04 1e 1203 3
+3e22 6 1205 3
+3e28 9 1206 3
+3e31 21 1208 3
+3e52 b 1210 3
+3e5d a 1212 3
+3e67 6 1213 3
+3e6d 1 1213 3
+FUNC 3e6e 65 0 -[MachSection initWithMachSection:andNumber:]
+3e6e 13 1219 3
+3e81 37 1220 3
+3eb8 9 1221 3
+3ec1 9 1222 3
+3eca 3 1225 3
+3ecd 6 1226 3
+3ed3 1 1226 3
+FUNC 3ed4 14 0 -[MachSection sectionPointer]
+3ed4 c 1228 3
+3ee0 6 1229 3
+3ee6 2 1230 3
+FUNC 3ee8 14 0 -[MachSection sectionNumber]
+3ee8 c 1232 3
+3ef4 6 1233 3
+3efa 2 1234 3
+FUNC 3efc 17c 0 -[DumpSymbols processDWARFSourceFileInfo:]
+3efc 14 459 3
+3f10 a 460 3
+3f1a 3c 461 3
+3f56 20 463 3
+3f76 5 464 3
+3f7b 3a 465 3
+3fb5 1d 466 3
+3fd2 3a 467 3
+400c 2a 468 3
+4036 3b 464 3
+4071 7 471 3
+FUNC 4078 1d7 0 DumpFunctionMap(std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >)
+4078 15 82 3
+408d 13 83 3
+40a0 1e 85 3
+40be 42 89 3
+4100 20 90 3
+4120 2b 91 3
+414b 1a 92 3
+4165 23 93 3
+4188 46 96 3
+41ce 46 99 3
+4214 33 83 3
+4247 8 102 3
+424f 1 102 3
+FUNC 4250 3ef 0 -[DumpSymbols processDWARFFunctionInfo:]
+4250 15 473 3
+4265 25 474 3
+428a 1e 476 3
+42a8 a 480 3
+42b2 3c 481 3
+42ee 3d 483 3
+432b 23 485 3
+434e 26 487 3
+4374 6 489 3
+437a 37 490 3
+43b1 2a 491 3
+43db 17 492 3
+43f2 30 496 3
+4422 3d 497 3
+445f 2e 498 3
+448d 30 502 3
+44bd 64 504 3
+4521 34 507 3
+4555 9d 509 3
+45f2 45 474 3
+4637 8 513 3
+463f 1 513 3
+FUNC 4640 1f5 0 -[DumpSymbols processDWARFLineNumberInfo:]
+4640 15 515 3
+4655 25 516 3
+467a 39 520 3
+46b3 26 521 3
+46d9 6 523 3
+46df 37 524 3
+4716 2a 525 3
+4740 17 526 3
+4757 30 529 3
+4787 61 531 3
+47e8 45 516 3
+482d 8 534 3
+4835 1 534 3
+FUNC 4836 10f 0 -[DumpSymbols dealloc]
+4836 13 1145 3
+4849 1c 1146 3
+4865 1c 1147 3
+4881 1c 1148 3
+489d 1c 1149 3
+48b9 1c 1150 3
+48d5 1c 1151 3
+48f1 25 1152 3
+4916 29 1154 3
+493f 6 1155 3
+4945 1 1155 3
+FUNC 4946 512 0 -[DumpSymbols loadDWARFSymbolInfo:offset:]
+4946 17 402 3
+495d 9 405 3
+4966 10 406 3
+4976 2b 408 3
+49a1 38 409 3
+49d9 3a 410 3
+4a13 2e 411 3
+4a41 31 416 3
+4a72 e 418 3
+4a80 24 420 3
+4aa4 5 422 3
+4aa9 b 424 3
+4ab4 b 425 3
+4abf e 426 3
+4acd 2b 427 3
+4af8 2b 428 3
+4b23 2c 431 3
+4b4f 52 439 3
+4ba1 34 444 3
+4bd5 1a 446 3
+4bef 21 451 3
+4c10 1e 452 3
+4c2e 21 453 3
+4c4f 40 422 3
+4c8f 6 453 3
+4c95 170 422 3
+4e05 43 456 3
+4e48 10 457 3
+FUNC 4e58 4fd 0 -[DumpSymbols generateSectionDictionary:]
+4e58 18 663 3
+4e70 10 665 3
+4e80 2e 666 3
+4eae 9 668 3
+4eb7 2b 669 3
+4ee2 7 670 3
+4ee9 2e 672 3
+4f17 d 676 3
+4f24 32 678 3
+4f56 29 680 3
+4f7f a 684 3
+4f89 3c 685 3
+4fc5 31 688 3
+4ff6 5d 689 3
+5053 26 692 3
+5079 21 694 3
+509a c 698 3
+50a6 e 699 3
+50b4 6 700 3
+50ba 9 701 3
+50c3 2e 702 3
+50f1 c 704 3
+50fd 3c 706 3
+5139 66 709 3
+519f 1c 712 3
+51bb fb 714 3
+52b6 6 717 3
+52bc 5 718 3
+52c1 19 704 3
+52da 25 714 3
+52ff 2e 722 3
+532d 9 723 3
+5336 17 698 3
+534d 8 725 3
+5355 1 725 3
+FUNC 5356 24a 0 -[DumpSymbols getSectionMapForArchitecture:]
+5356 14 643 3
+536a 43 645 3
+53ad 1a 648 3
+53c7 1c 645 3
+53e3 18 648 3
+53fb 40 650 3
+543b 20 651 3
+545b 17 652 3
+5472 16 651 3
+5488 cb 652 3
+5553 11 654 3
+5564 32 657 3
+5596 a 658 3
+FUNC 55a0 3fe 0 -[DumpSymbols initWithContentsOfFile:]
+55a0 14 1056 3
+55b4 3b 1057 3
+55ef 44 1059 3
+5633 17 1060 3
+564a c 1061 3
+5656 1f 1064 3
+5675 2b 1067 3
+56a0 a 1069 3
+56aa 35 1083 3
+56df 2 1087 3
+56e1 1a 1088 3
+56fb 3d 1087 3
+5738 33 1092 3
+576b 6 1094 3
+5771 e 1095 3
+577f 17 1096 3
+5796 c 1097 3
+57a2 1c 1101 3
+57be 1f 1103 3
+57dd 18 1104 3
+57f5 23 1107 3
+5818 25 1109 3
+583d 1c 1107 3
+5859 17 1110 3
+5870 c 1111 3
+587c 2a 1115 3
+58a6 8 1116 3
+58ae a 1118 3
+58b8 9 1119 3
+58c1 d 1122 3
+58ce 29 1124 3
+58f7 20 1126 3
+5917 20 1128 3
+5937 57 1132 3
+598e 9 1136 3
+5997 7 1137 3
+FUNC 599e d74 0 -[DumpSymbols outputSymbolFile:]
+599e 18 877 3
+59b6 2e 879 3
+59e4 30 880 3
+5a14 5d 882 3
+5a71 30 883 3
+5aa1 5d 885 3
+5afe 2e 888 3
+5b2c 38 891 3
+5b64 46 892 3
+5baa 26 893 3
+5bd0 20 895 3
+5bf0 20 904 3
+5c10 30 898 3
+5c40 f 899 3
+5c4f 1e 904 3
+5c6d 17 907 3
+5c84 17 908 3
+5c9b 44 911 3
+5cdf 44 914 3
+5d23 a 917 3
+5d2d 36 921 3
+5d63 30 923 3
+5d93 9 18 4
+5d9c 9 19 4
+5da5 c 20 4
+5db1 56 923 3
+5e07 74 925 3
+5e7b f 927 3
+5e8a 44 932 3
+5ece 20 933 3
+5eee c 934 3
+5efa 4e 935 3
+5f48 41 936 3
+5f89 f 937 3
+5f98 14 934 3
+5fac 7 941 3
+5fb3 14 942 3
+5fc7 14 943 3
+5fdb 1d 946 3
+5ff8 c 948 3
+6004 24 949 3
+6028 29 950 3
+6051 9 953 3
+605a 28 954 3
+6082 2e 955 3
+60b0 1e 957 3
+60ce 7 959 3
+60d5 26 962 3
+60fb 2a 963 3
+6125 2a 964 3
+614f 6 966 3
+6155 2a 967 3
+617f e 971 3
+618d 43 972 3
+61d0 4c 974 3
+621c 8 975 3
+6224 2e 979 3
+6252 2e 982 3
+6280 2e 985 3
+62ae 2e 988 3
+62dc 2e 991 3
+630a 2e 994 3
+6338 2e 997 3
+6366 2e 1000 3
+6394 54 1004 3
+63e8 c 1005 3
+63f4 e 1007 3
+6402 27 1008 3
+6429 8 1009 3
+6431 34 1010 3
+6465 24 1012 3
+6489 2 1013 3
+648b 2a 1017 3
+64b5 a 1019 3
+64bf 14 1020 3
+64d3 1d 1021 3
+64f0 a 1025 3
+64fa 32 1026 3
+652c 33 1028 3
+655f c 1029 3
+656b 55 1034 3
+65c0 f 1036 3
+65cf 16 1040 3
+65e5 61 1041 3
+6646 f 1043 3
+6655 47 1046 3
+669c c 1048 3
+66a8 11 948 3
+66b9 4e 1052 3
+6707 b 1053 3
+FUNC 6712 11 0 operator new(unsigned long, void*)
+6712 c 94 5
+671e 5 94 5
+6723 1 94 5
+FUNC 6724 e 0 operator delete(void*, void*)
+6724 c 98 5
+6730 2 98 5
+673e 7 76 6
+6745 2 77 6
+6747 1a 78 6
+6761 d 77 6
+676e 3 79 6
+6771 2 80 6
+6773 1 80 6
+6780 d 95 6
+678d 1 95 6
+678e 13 127 74
+67a1 2a 127 74
+67cb 1 127 74
+67cc 13 127 74
+67df 2a 127 74
+6809 1 127 74
+680a 13 127 74
+681d 2a 127 74
+6847 1 127 74
+FUNC 6848 e 0 dwarf2reader::LineInfoHandler::DefineDir(std::string const&, unsigned int)
+6848 c 131 7
+6854 2 131 74
+FUNC 6856 26 0 dwarf2reader::LineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long)
+6856 24 142 7
+687a 2 142 74
+FUNC 687c 1a 0 dwarf2reader::LineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int)
+687c 18 150 7
+6894 2 150 74
+6896 12 299 74
+68a8 12 299 74
+68ba 13 301 74
+68cd 2a 301 74
+68f7 1 301 74
+68f8 13 301 74
+690b 2a 301 74
+6935 1 301 74
+6936 13 301 74
+6949 2a 301 74
+6973 1 301 74
+FUNC 6974 44 0 dwarf2reader::Dwarf2Handler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char)
+6974 39 308 7
+69ad b 308 74
+FUNC 69b8 1f 0 dwarf2reader::Dwarf2Handler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > > const&)
+69b8 18 314 7
+69d0 7 314 74
+69d7 1 314 74
+FUNC 69d8 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long)
+69d8 24 323 7
+69fc 2 323 74
+FUNC 69fe 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeSigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, long long)
+69fe 24 332 7
+6a22 2 332 74
+FUNC 6a24 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeBuffer(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, char const*, unsigned long long)
+6a24 24 345 7
+6a48 2 345 74
+FUNC 6a4a 1a 0 dwarf2reader::Dwarf2Handler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&)
+6a4a 18 354 7
+6a62 2 354 74
+FUNC 6a64 1a 0 dwarf2reader::Dwarf2Handler::EndDIE(unsigned long long)
+6a64 18 360 7
+6a7c 2 360 74
+6a7e c 44 8
+6a8a 2 44 8
+6a8c 13 55 32
+6a9f 35 55 32
+6ad4 13 91 32
+6ae7 73 96 32
+6b5a 13 98 32
+6b6d 35 98 32
+6bae 1a 75 3
+6bc8 2 76 3
+FUNC 6bca 20 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator!=(std::_Rb_tree_const_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > const&) const
+6bca c 287 10
+6bd6 14 288 40
+FUNC 6bea 16 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator->() const
+6bea c 249 10
+6bf6 a 250 40
+6c0c 7 614 72
+6c13 1 614 72
+6c14 c 241 40
+6c20 c 242 40
+FUNC 6c2c 16 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator*() const
+6c2c c 245 11
+6c38 a 246 40
+6c42 c 241 40
+6c4e c 242 40
+FUNC 6c5a 20 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator!=(std::_Rb_tree_const_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > const&) const
+6c5a c 287 11
+6c66 14 288 40
+FUNC 6c7a 16 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator->() const
+6c7a c 249 11
+6c86 a 250 40
+6c90 c 185 34
+6c9c 18 186 34
+6cc0 14 204 34
+6cd4 c 69 70
+6ce0 d 69 70
+6ced 1 69 70
+6cee c 89 70
+6cfa 20 90 70
+6d1a c 69 70
+6d26 d 69 70
+6d33 1 69 70
+6d34 c 69 70
+6d40 d 69 70
+6d4d 1 69 70
+FUNC 6d4e 25 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator++()
+6d4e c 253 13
+6d5a 14 255 40
+6d6e 5 256 40
+6d73 1 256 40
+FUNC 6d74 25 0 std::_Rb_tree_const_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator++()
+6d74 c 253 13
+6d80 14 255 40
+6d94 5 256 40
+6d99 1 256 40
+FUNC 6d9a 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_begin()
+6d9a c 461 13
+6da6 8 462 40
+FUNC 6dae 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_begin()
+6dae c 461 13
+6dba 8 462 40
+6dc2 c 65 68
+6dce 2 65 68
+6dd0 c 72 68
+6ddc 2 72 68
+6dde c 97 69
+6dea d 97 69
+6df7 1 97 69
+6df8 c 105 69
+6e04 d 105 69
+6e11 1 105 69
+6e12 c 105 69
+6e1e d 105 69
+6e2b 1 105 69
+6e2c c 67 68
+6e38 2 67 68
+6e3a c 99 69
+6e46 14 100 69
+6e5a c 99 69
+6e66 14 100 69
+FUNC 6e7a 2b 0 std::_Vector_base<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::get_allocator() const
+6e7a 10 93 16
+6e8a 1b 94 71
+6ea5 1 94 71
+6ea6 c 65 68
+6eb2 2 65 68
+6eb4 c 72 68
+6ec0 2 72 68
+6ec2 c 97 69
+6ece d 97 69
+6edb 1 97 69
+6edc c 105 69
+6ee8 d 105 69
+6ef5 1 105 69
+6ef6 c 105 69
+6f02 d 105 69
+6f0f 1 105 69
+6f10 c 67 68
+6f1c 2 67 68
+6f1e c 99 69
+6f2a 14 100 69
+6f3e c 99 69
+6f4a 14 100 69
+FUNC 6f5e 2b 0 std::_Vector_base<std::string, std::allocator<std::string> >::get_allocator() const
+6f5e 10 93 16
+6f6e 1b 94 71
+6f89 1 94 71
+6f8a c 603 72
+6f96 c 603 72
+FUNC 6fa2 23 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::begin()
+6fa2 c 333 16
+6fae 17 334 71
+6fc5 1 334 71
+FUNC 6fc6 26 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::end()
+6fc6 c 351 16
+6fd2 1a 352 71
+6ff8 5 666 72
+6ffd 1 666 72
+6ffe c 608 72
+700a 14 609 72
+702a 5 666 72
+702f 1 666 72
+FUNC 7030 35 0 bool __gnu_cxx::operator!=<dwarf2reader::SourceFileInfo const*, dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo const*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > const&, __gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > const&)
+7030 d 693 16
+703d 28 694 72
+7065 1 694 72
+7066 c 603 72
+7072 c 603 72
+708a 27 629 72
+70b1 1 629 72
+70b2 c 84 70
+70be 1f 85 70
+70dd 1 85 70
+FUNC 70de 32 0 std::pair<std::string, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> std::make_pair<std::string, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*>(std::string, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*)
+70de 10 144 16
+70ee 22 145 70
+711c a 190 34
+7132 d 194 34
+713f 1 194 34
+7140 c 84 70
+714c 17 85 70
+7163 1 85 70
+FUNC 7164 2d 0 std::pair<char const*, unsigned long> std::make_pair<char const*, unsigned long>(char const*, unsigned long)
+7164 c 144 16
+7170 21 145 70
+7191 1 145 70
+7192 c 84 70
+719e 1d 85 70
+71bb 1 85 70
+FUNC 71bc 30 0 std::pair<char*, std::pair<char const*, unsigned long> > std::make_pair<char*, std::pair<char const*, unsigned long> >(char*, std::pair<char const*, unsigned long>)
+71bc 10 144 16
+71cc 20 145 70
+71ec c 89 70
+71f8 20 90 70
+7218 d 89 70
+7225 70 90 70
+7295 1 90 70
+FUNC 7296 12 0 std::iterator_traits<unsigned long const*>::iterator_category std::__iterator_category<unsigned long const*>(unsigned long const* const&)
+7296 c 164 17
+72a2 6 165 17
+FUNC 72a8 1d 0 std::iterator_traits<unsigned long const*>::difference_type std::__distance<unsigned long const*>(unsigned long const*, unsigned long const*, std::random_access_iterator_tag)
+72a8 c 92 18
+72b4 11 97 18
+72c5 1 97 18
+FUNC 72c6 33 0 std::iterator_traits<unsigned long const*>::difference_type std::distance<unsigned long const*>(unsigned long const*, unsigned long const*)
+72c6 c 114 18
+72d2 27 118 18
+72f9 1 118 18
+FUNC 72fa 20 0 void std::__advance<unsigned long const*, int>(unsigned long const*&, int, std::random_access_iterator_tag)
+72fa c 150 18
+7306 14 155 18
+FUNC 731a 33 0 void std::advance<unsigned long const*, int>(unsigned long const*&, int)
+731a c 172 18
+7326 27 175 18
+734d 1 175 18
+FUNC 734e 7a 0 unsigned long const* std::lower_bound<unsigned long const*, unsigned long>(unsigned long const*, unsigned long const*, unsigned long const&)
+734e c 2625 19
+735a 15 2642 19
+736f 2 2646 19
+7371 8 2648 19
+7379 6 2649 19
+737f 12 2650 19
+7391 e 2651 19
+739f 6 2653 19
+73a5 4 2654 19
+73a9 e 2655 19
+73b7 6 2658 19
+73bd 6 2646 19
+73c3 5 2660 19
+73db b 227 34
+73e6 e 228 34
+73f4 1c 229 34
+7410 20 230 34
+7430 6 231 34
+7436 c 72 68
+7442 2 72 68
+7444 c 105 69
+7450 d 105 69
+745d 1 105 69
+745e c 105 69
+746a d 105 69
+7477 1 105 69
+7478 c 80 71
+7484 d 80 71
+7491 1 80 71
+7492 c 67 68
+749e 2 67 68
+74a0 c 99 69
+74ac 14 100 69
+FUNC 74c0 2b 0 std::_Vector_base<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::get_allocator() const
+74c0 10 93 19
+74d0 1b 94 71
+74eb 1 94 71
+74ec c 238 40
+74f8 a 239 40
+FUNC 7502 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::begin() const
+7502 c 585 19
+750e 1a 588 40
+FUNC 7528 19 0 std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::begin() const
+7528 c 243 20
+7534 d 244 45
+7541 1 244 45
+FUNC 7542 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::end() const
+7542 c 596 20
+754e 1a 597 40
+FUNC 7568 19 0 std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::end() const
+7568 c 260 20
+7574 d 261 45
+7581 1 261 45
+7582 c 65 68
+758e 2 65 68
+7590 c 72 68
+759c 2 72 68
+759e c 97 69
+75aa d 97 69
+75b7 1 97 69
+75b8 c 105 69
+75c4 d 105 69
+75d1 1 105 69
+75d2 c 72 68
+75de 2 72 68
+75e0 c 105 69
+75ec d 105 69
+75f9 1 105 69
+75fa c 397 40
+7606 d 397 40
+7613 1 397 40
+7614 c 105 69
+7620 d 105 69
+762d 1 105 69
+FUNC 762e 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_right(std::_Rb_tree_node_base*)
+762e c 496 20
+763a 8 497 40
+FUNC 7642 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_left(std::_Rb_tree_node_base*)
+7642 c 488 20
+764e 8 489 40
+7656 c 65 68
+7662 2 65 68
+7664 c 72 68
+7670 2 72 68
+7672 c 97 69
+767e d 97 69
+768b 1 97 69
+768c c 105 69
+7698 d 105 69
+76a5 1 105 69
+76a6 c 72 68
+76b2 2 72 68
+76b4 c 105 69
+76c0 d 105 69
+76cd 1 105 69
+76ce c 397 40
+76da d 397 40
+76e7 1 397 40
+76e8 c 105 69
+76f4 d 105 69
+7701 1 105 69
+FUNC 7702 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_right(std::_Rb_tree_node_base*)
+7702 c 496 20
+770e 8 497 40
+FUNC 7716 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_left(std::_Rb_tree_node_base*)
+7716 c 488 20
+7722 8 489 40
+772a c 84 71
+7736 2f 85 71
+7765 2 86 71
+7767 1 86 71
+7768 c 80 71
+7774 d 80 71
+7781 1 80 71
+7782 c 96 71
+778e 12 97 71
+77a0 2 98 71
+77a2 c 84 71
+77ae 2f 85 71
+77dd 2 86 71
+77df 1 86 71
+77e0 c 80 71
+77ec d 80 71
+77f9 1 80 71
+77fa c 96 71
+7806 12 97 71
+7818 2 98 71
+7826 d 107 68
+7833 1 107 68
+FUNC 7834 2e 0 void std::_Destroy<std::string*, std::allocator<std::string> >(std::string*, std::string*, std::allocator<std::string>)
+7834 c 171 21
+7840 2 173 73
+7842 12 174 73
+7854 c 173 73
+7860 2 174 73
+7862 c 167 40
+786e a 168 40
+FUNC 7878 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::begin()
+7878 c 581 21
+7884 1a 582 40
+FUNC 789e 19 0 std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::begin()
+789e c 234 21
+78aa d 235 45
+78b7 1 235 45
+FUNC 78b8 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::end()
+78b8 c 592 21
+78c4 1a 593 40
+FUNC 78de 19 0 std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::end()
+78de c 251 21
+78ea d 252 45
+78f7 1 252 45
+78f8 c 167 40
+7904 a 168 40
+FUNC 790e 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::begin()
+790e c 581 21
+791a 1a 582 40
+FUNC 7934 19 0 std::map<unsigned long long, std::pair<std::string, unsigned int>, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::begin()
+7934 c 234 21
+7940 d 235 45
+794d 1 235 45
+FUNC 794e 26 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::end()
+794e c 592 21
+795a 1a 593 40
+FUNC 7974 19 0 std::map<unsigned long long, std::pair<std::string, unsigned int>, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::end()
+7974 c 251 21
+7980 d 252 45
+798d 1 252 45
+FUNC 798e 11 0 std::_Select1st<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >::operator()(std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*>&) const
+798e c 546 22
+799a 5 547 41
+799f 1 547 41
+79a0 c 128 34
+79ac 13 129 34
+79bf 1 129 34
+79cc 22 396 34
+79fa d 199 42
+7a07 1 199 42
+7a08 c 65 68
+7a14 2 65 68
+7a16 c 72 68
+7a22 2 72 68
+7a24 c 97 69
+7a30 d 97 69
+7a3d 1 97 69
+7a3e c 105 69
+7a4a d 105 69
+7a57 1 105 69
+7a58 c 65 68
+7a64 2 65 68
+7a66 c 72 68
+7a72 2 72 68
+7a74 c 105 69
+7a80 d 105 69
+7a8d 1 105 69
+7a8e c 97 69
+7a9a d 97 69
+7aa7 1 97 69
+7aa8 c 72 68
+7ab4 2 72 68
+7ab6 c 105 69
+7ac2 d 105 69
+7acf 1 105 69
+7adc d 94 68
+7ae9 1 94 68
+FUNC 7aea 2f 0 std::_Vector_base<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::_M_deallocate(dwarf2reader::CompilationUnit::Abbrev*, unsigned long)
+7aea c 120 23
+7af6 6 122 71
+7afc 1d 123 71
+7b19 1 123 71
+7b1a c 108 71
+7b26 43 109 71
+7b69 1 109 71
+7b6a c 65 68
+7b76 2 65 68
+7b78 c 103 69
+7b84 d 103 69
+7b91 1 103 69
+7b92 c 65 68
+7b9e 2 65 68
+7ba0 c 103 69
+7bac d 103 69
+7bb9 1 103 69
+7bc6 d 94 68
+7bd3 1 94 68
+FUNC 7bd4 2f 0 std::_Vector_base<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::_M_deallocate(dwarf2reader::SourceFileInfo*, unsigned long)
+7bd4 c 120 23
+7be0 6 122 71
+7be6 1d 123 71
+7c03 1 123 71
+7c04 c 108 71
+7c10 43 109 71
+7c53 1 109 71
+7c54 c 188 71
+7c60 12 189 71
+7c72 2 190 71
+7c74 c 35 32
+7c80 d 35 32
+7c8d 1 35 32
+7c9a d 107 68
+7ca7 1 107 68
+FUNC 7ca8 2e 0 void std::_Destroy<dwarf2reader::SourceFileInfo*, std::allocator<dwarf2reader::SourceFileInfo> >(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, std::allocator<dwarf2reader::SourceFileInfo>)
+7ca8 c 171 23
+7cb4 2 173 73
+7cb6 12 174 73
+7cc8 c 173 73
+7cd4 2 174 73
+7cd6 d 272 71
+7ce3 8c 273 71
+7d6f 1 273 71
+7d7c d 94 68
+7d89 1 94 68
+FUNC 7d8a 2f 0 std::_Vector_base<std::string, std::allocator<std::string> >::_M_deallocate(std::string*, unsigned long)
+7d8a c 120 23
+7d96 6 122 71
+7d9c 1d 123 71
+7db9 1 123 71
+7dba c 108 71
+7dc6 3d 109 71
+7e03 1 109 71
+7e04 c 188 71
+7e10 12 189 71
+7e22 2 190 71
+7e24 d 272 71
+7e31 8c 273 71
+7ebd 1 273 71
+7eca 2b 596 34
+7ef5 1 596 34
+7f02 7 614 72
+7f09 1 614 72
+7f0a c 65 68
+7f16 2 65 68
+7f18 c 72 68
+7f24 2 72 68
+7f26 c 103 69
+7f32 d 103 69
+7f3f 1 103 69
+7f40 c 105 69
+7f4c d 105 69
+7f59 1 105 69
+7f5a c 65 68
+7f66 2 65 68
+7f68 c 72 68
+7f74 2 72 68
+7f76 c 103 69
+7f82 d 103 69
+7f8f 1 103 69
+7f90 c 105 69
+7f9c d 105 69
+7fa9 1 105 69
+7faa c 105 69
+7fb6 d 105 69
+7fc3 1 105 69
+7fd0 d 575 34
+7fdd 1 575 34
+7fea d 575 34
+7ff7 1 575 34
+FUNC 7ff8 11 0 std::_Select1st<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >::operator()(std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> const&) const
+7ff8 c 550 23
+8004 5 551 41
+8009 1 551 41
+8016 2f 600 34
+8045 1 600 34
+8046 c 84 70
+8052 1e 85 70
+FUNC 8070 11 0 std::_Select1st<std::pair<std::string const, std::pair<char const*, unsigned long long> > >::operator()(std::pair<std::string const, std::pair<char const*, unsigned long long> >&) const
+8070 c 546 23
+807c 5 547 41
+8081 1 547 41
+FUNC 8082 11 0 std::_Select1st<std::pair<std::string const, std::pair<char const*, unsigned long long> > >::operator()(std::pair<std::string const, std::pair<char const*, unsigned long long> > const&) const
+8082 c 550 23
+808e 5 551 41
+8093 1 551 41
+8094 c 128 34
+80a0 13 129 34
+80b3 1 129 34
+80b4 c 84 70
+80c0 1e 85 70
+80de c 65 68
+80ea 2 65 68
+80ec c 103 69
+80f8 d 103 69
+8105 1 103 69
+8106 c 65 68
+8112 2 65 68
+8114 c 72 68
+8120 2 72 68
+8122 c 105 69
+812e d 105 69
+813b 1 105 69
+813c c 103 69
+8148 d 103 69
+8155 1 103 69
+8156 c 105 69
+8162 d 105 69
+816f 1 105 69
+8170 c 80 71
+817c d 80 71
+8189 1 80 71
+818a c 67 68
+8196 2 67 68
+8198 c 99 69
+81a4 14 100 69
+FUNC 81b8 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::get_allocator() const
+81b8 10 93 23
+81c8 1b 94 71
+81e3 1 94 71
+81e4 c 99 69
+81f0 14 100 69
+8210 2 107 68
+FUNC 8212 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>)
+8212 c 171 23
+821e 2 173 73
+8220 12 174 73
+8232 c 173 73
+823e 2 174 73
+824c d 107 68
+8259 1 107 68
+825a c 67 68
+8266 2 67 68
+8268 c 99 69
+8274 14 100 69
+8288 c 403 40
+8294 1c 404 40
+82b0 a 406 40
+82ba a 407 40
+82c4 c 408 40
+82d0 e 409 40
+82de c 553 40
+82ea 36 554 40
+8320 2 555 40
+8322 c 103 69
+832e d 103 69
+833b 1 103 69
+FUNC 833c 2b 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::get_allocator() const
+833c 10 350 23
+834c 1b 351 40
+8367 1 351 40
+8368 c 69 70
+8374 2 69 70
+8382 d 107 68
+838f 1 107 68
+839c d 94 68
+83a9 1 94 68
+FUNC 83aa 2a 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_put_node(std::_Rb_tree_node<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >*)
+83aa c 359 23
+83b6 1e 360 40
+FUNC 83d4 59 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::destroy_node(std::_Rb_tree_node<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >*)
+83d4 d 387 23
+83e1 35 389 40
+8416 17 390 40
+842d 1 390 40
+FUNC 842e 56 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_erase(std::_Rb_tree_node<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >*)
+842e c 1051 23
+843a 2 1054 40
+843c 1a 1056 40
+8456 e 1057 40
+8464 12 1058 40
+8476 6 1059 40
+847c 6 1054 40
+8482 2 1059 40
+8484 d 569 40
+8491 58 570 40
+84e9 1 570 40
+84ea c 147 45
+84f6 31 148 45
+8527 1 148 45
+8528 c 92 45
+8534 d 92 45
+8541 1 92 45
+8542 c 67 68
+854e 2 67 68
+8550 c 99 69
+855c 14 100 69
+8570 c 403 40
+857c 1c 404 40
+8598 a 406 40
+85a2 a 407 40
+85ac c 408 40
+85b8 e 409 40
+85c6 c 553 40
+85d2 36 554 40
+8608 2 555 40
+860a c 103 69
+8616 d 103 69
+8623 1 103 69
+FUNC 8624 2b 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::get_allocator() const
+8624 10 350 23
+8634 1b 351 40
+864f 1 351 40
+8650 c 69 70
+865c d 69 70
+8669 1 69 70
+866a c 69 70
+8676 30 69 70
+86b2 d 107 68
+86bf 1 107 68
+86cc d 94 68
+86d9 1 94 68
+FUNC 86da 2a 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_put_node(std::_Rb_tree_node<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >*)
+86da c 359 23
+86e6 1e 360 40
+FUNC 8704 59 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::destroy_node(std::_Rb_tree_node<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >*)
+8704 d 387 23
+8711 35 389 40
+8746 17 390 40
+875d 1 390 40
+FUNC 875e 56 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_erase(std::_Rb_tree_node<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >*)
+875e c 1051 23
+876a 2 1054 40
+876c 1a 1056 40
+8786 e 1057 40
+8794 12 1058 40
+87a6 6 1059 40
+87ac 6 1054 40
+87b2 2 1059 40
+87b4 d 569 40
+87c1 58 570 40
+8819 1 570 40
+881a c 147 45
+8826 31 148 45
+8857 1 148 45
+8858 c 92 45
+8864 d 92 45
+8871 1 92 45
+8872 c 603 72
+887e c 603 72
+FUNC 888a 23 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::begin()
+888a c 333 23
+8896 17 334 71
+88ad 1 334 71
+88ba 2a 654 72
+FUNC 88e4 42 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::operator[](unsigned long)
+88e4 c 494 23
+88f0 36 495 71
+FUNC 8926 26 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::end()
+8926 c 351 23
+8932 1a 352 71
+FUNC 894c 28 0 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
+894c c 2115 24
+8958 1c 2116 37
+FUNC 8974 23 0 std::equal_to<std::string>::operator()(std::string const&, std::string const&) const
+8974 c 199 24
+8980 17 200 41
+8997 1 200 41
+8998 c 80 71
+89a4 d 80 71
+89b1 1 80 71
+89b2 c 67 68
+89be 2 67 68
+89c0 c 99 69
+89cc 14 100 69
+FUNC 89e0 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::get_allocator() const
+89e0 10 93 24
+89f0 1b 94 71
+8a0b 1 94 71
+8a0c c 99 69
+8a18 14 100 69
+8a2c c 84 71
+8a38 2f 85 71
+8a67 2 86 71
+8a69 1 86 71
+8a6a c 96 71
+8a76 12 97 71
+8a88 2 98 71
+8a96 2 107 68
+FUNC 8a98 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>)
+8a98 c 171 24
+8aa4 2 173 73
+8aa6 12 174 73
+8ab8 c 173 73
+8ac4 2 174 73
+FUNC 8ac6 13 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::max_size() const
+8ac6 c 407 24
+8ad2 7 408 71
+8ad9 1 408 71
+8ada c 603 72
+8ae6 c 603 72
+FUNC 8af2 26 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::end()
+8af2 c 351 24
+8afe 1a 352 71
+FUNC 8b18 23 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::begin()
+8b18 c 333 24
+8b24 17 334 71
+8b3b 1 334 71
+8b48 2a 654 72
+8b7e 7 614 72
+8b85 1 614 72
+FUNC 8b86 42 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::operator[](unsigned long)
+8b86 c 494 24
+8b92 36 495 71
+8bd4 d 107 68
+8be1 1 107 68
+FUNC 8be2 28 0 void std::swap<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**&, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**&)
+8be2 c 92 25
+8bee 8 97 61
+8bf6 a 98 61
+8c00 a 99 61
+FUNC 8c0a 50 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >&)
+8c0a c 733 25
+8c16 12 735 71
+8c28 18 736 71
+8c40 1a 737 71
+8c66 2b 596 34
+8c91 1 596 34
+8c9e 2f 600 34
+8ccd 1 600 34
+FUNC 8cce 28 0 void std::swap<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**&, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**&)
+8cce c 92 25
+8cda 8 97 61
+8ce2 a 98 61
+8cec a 99 61
+FUNC 8cf6 50 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >&)
+8cf6 c 733 25
+8d02 12 735 71
+8d14 18 736 71
+8d2c 1a 737 71
+8d46 c 84 71
+8d52 2f 85 71
+8d81 2 86 71
+8d83 1 86 71
+8d84 c 96 71
+8d90 12 97 71
+8da2 2 98 71
+FUNC 8da4 13 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::max_size() const
+8da4 c 407 25
+8db0 7 408 71
+8db7 1 408 71
+8dc4 d 94 68
+8dd1 1 94 68
+FUNC 8dd2 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long)
+8dd2 c 120 25
+8dde 6 122 71
+8de4 1d 123 71
+8e01 1 123 71
+8e02 c 108 71
+8e0e 3d 109 71
+8e4b 1 109 71
+8e4c c 272 71
+8e58 4b 273 71
+8ea3 1 273 71
+8ea4 c 188 71
+8eb0 12 189 71
+8ec2 2 190 71
+8ec4 c 603 72
+8ed0 c 603 72
+FUNC 8edc 2b 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::begin() const
+8edc c 342 25
+8ee8 1f 343 71
+8f07 1 343 71
+FUNC 8f08 2c 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::end() const
+8f08 c 360 25
+8f14 20 361 71
+8f40 5 666 72
+8f45 1 666 72
+8f53 2b 759 72
+FUNC 8f7e 3c 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::size() const
+8f7e c 402 25
+8f8a 30 403 71
+8fc6 26 588 34
+8ff8 15 511 34
+900d 79 513 34
+9086 21 517 34
+90a7 1 517 34
+90b4 14 225 42
+90d4 26 592 34
+FUNC 90fa 49 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::capacity() const
+90fa c 449 25
+9106 3d 451 71
+9143 1 451 71
+9144 c 103 69
+9150 d 103 69
+915d 1 103 69
+916e 1b 286 34
+9189 1 286 34
+9196 d 94 68
+91a3 1 94 68
+91b0 1e 301 34
+91db 56 622 34
+9231 17 623 34
+9254 9 1080 34
+925d 1a 1082 34
+9277 2 1083 34
+9279 8 1085 34
+9281 12 1086 34
+9293 6 1087 34
+9299 6 1083 34
+929f 1b 1089 34
+92ba 1d 1080 34
+92d7 c 1091 34
+92e3 1 1091 34
+92e4 d 360 34
+92f1 77 361 34
+9368 c 93 42
+9374 d 93 42
+9381 1 93 42
+9382 c 72 68
+938e 2 72 68
+9390 c 105 69
+939c d 105 69
+93a9 1 105 69
+93aa c 301 66
+93b6 d 301 66
+93c3 1 301 66
+93d0 d 94 68
+93dd 1 94 68
+FUNC 93de 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long)
+93de c 120 26
+93ea 6 122 71
+93f0 1d 123 71
+940d 1 123 71
+940e c 108 71
+941a 3d 109 71
+9457 1 109 71
+9458 c 188 71
+9464 12 189 71
+9476 2 190 71
+9478 c 272 71
+9484 4b 273 71
+94cf 1 273 71
+94d0 c 603 72
+94dc c 603 72
+FUNC 94e8 2b 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::begin() const
+94e8 c 342 26
+94f4 1f 343 71
+9513 1 343 71
+FUNC 9514 2c 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::end() const
+9514 c 360 26
+9520 20 361 71
+954c 2d 662 72
+9579 1 662 72
+FUNC 957a 2d 0 unsigned long const& std::max<unsigned long>(unsigned long const&, unsigned long const&)
+957a c 206 26
+9586 e 211 61
+9594 8 212 61
+959c b 213 61
+95a7 1 213 61
+95b4 19 650 72
+95cd 1 650 72
+95da 5 666 72
+95df 1 666 72
+95ed 2b 759 72
+9624 5 666 72
+9629 1 666 72
+9637 2b 759 72
+FUNC 9662 49 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::capacity() const
+9662 c 449 26
+966e 3d 451 71
+96ab 1 451 71
+FUNC 96ac 3c 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::size() const
+96ac c 402 26
+96b8 30 403 71
+96f4 26 588 34
+9726 26 592 34
+974c c 103 69
+9758 d 103 69
+9765 1 103 69
+9776 1b 286 34
+9791 1 286 34
+979e d 94 68
+97ab 1 94 68
+97b8 1e 301 34
+97e3 56 622 34
+9839 17 623 34
+985c 9 1080 34
+9865 1a 1082 34
+987f 2 1083 34
+9881 8 1085 34
+9889 12 1086 34
+989b 6 1087 34
+98a1 6 1083 34
+98a7 1b 1089 34
+98c2 1d 1080 34
+98df c 1091 34
+98eb 1 1091 34
+98ec d 360 34
+98f9 77 361 34
+9970 c 69 70
+997c 20 69 70
+99a9 5c 104 68
+9a05 1 104 68
+9a06 c 69 70
+9a12 2c 69 70
+9a4b 5c 104 68
+9aa7 1 104 68
+9ab4 2d 662 72
+9ae1 1 662 72
+9aee 19 650 72
+9b07 1 650 72
+9b14 5 666 72
+9b19 1 666 72
+9b27 2b 759 72
+9b52 c 72 68
+9b5e 2 72 68
+9b60 c 105 69
+9b6c d 105 69
+9b79 1 105 69
+9b7a c 69 70
+9b86 2 69 70
+9b94 d 107 68
+9ba1 1 107 68
+9bae d 94 68
+9bbb 1 94 68
+FUNC 9bbc 2a 0 std::_List_base<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_put_node(std::_List_node<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >*)
+9bbc c 315 26
+9bc8 1e 316 66
+FUNC 9be6 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > > const&)
+9be6 d 699 26
+9bf3 28 700 72
+9c1b 1 700 72
+9c28 d 623 72
+9c35 5 624 72
+FUNC 9c3a 4b 0 void std::__fill<true>::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+9c3a c 539 61
+9c46 8 541 61
+9c4e 2 542 61
+9c50 12 543 61
+9c62 21 542 61
+9c83 2 543 61
+9c85 1 543 61
+FUNC 9c86 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+9c86 c 560 26
+9c92 4 567 61
+9c96 1b 568 61
+9cb1 1 568 61
+FUNC 9cb2 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>)
+9cb2 c 171 26
+9cbe 2 173 73
+9cc0 1a 174 73
+9cda 21 173 73
+9cfb 2 174 73
+9cfd 1 174 73
+9d0a 7 98 68
+9d11 1 98 68
+9d1e 1d 85 68
+9d3b 5 86 68
+9d40 16 88 68
+9d62 1d 297 34
+9d7f 1 297 34
+9d8d e 605 34
+9d9b 9 606 34
+9da4 3c 609 34
+9de0 b 610 34
+9deb 11 609 34
+9dfc b 612 34
+9e07 12 614 34
+9e19 b 615 34
+9e24 13 612 34
+9e37 8 615 34
+9e3f 1 615 34
+9e4d 15 751 34
+9e62 1a 752 34
+9e7c b 754 34
+9e87 49 755 34
+9ed0 3b 756 34
+9f0b 12 754 34
+9f1d 15 758 34
+9f32 8 759 34
+9f3a 1c 760 34
+9f56 f 761 34
+9f65 41 762 34
+9fb2 7 98 68
+9fb9 1 98 68
+9fc6 1d 85 68
+9fe3 5 86 68
+9fe8 17 88 68
+9fff 1 88 68
+a00c 1d 297 34
+a029 1 297 34
+a037 e 605 34
+a045 9 606 34
+a04e 3c 609 34
+a08a b 610 34
+a095 11 609 34
+a0a6 b 612 34
+a0b1 12 614 34
+a0c3 b 615 34
+a0ce 13 612 34
+a0e1 8 615 34
+a0e9 1 615 34
+a0f7 15 751 34
+a10c 1a 752 34
+a126 b 754 34
+a131 49 755 34
+a17a 3b 756 34
+a1b5 12 754 34
+a1c7 15 758 34
+a1dc 8 759 34
+a1e4 1c 760 34
+a200 f 761 34
+a20f 41 762 34
+FUNC a250 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > > const&)
+a250 d 699 26
+a25d 28 700 72
+a285 1 700 72
+a292 d 623 72
+a29f 5 624 72
+FUNC a2a4 4b 0 void std::__fill<true>::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+a2a4 c 539 61
+a2b0 8 541 61
+a2b8 2 542 61
+a2ba 12 543 61
+a2cc 21 542 61
+a2ed 2 543 61
+a2ef 1 543 61
+FUNC a2f0 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+a2f0 c 560 26
+a2fc 4 567 61
+a300 1b 568 61
+a31b 1 568 61
+FUNC a31c 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>)
+a31c c 171 26
+a328 2 173 73
+a32a 1a 174 73
+a344 21 173 73
+a365 2 174 73
+a367 1 174 73
+a368 c 65 68
+a374 2 65 68
+a376 c 103 69
+a382 d 103 69
+a38f 1 103 69
+FUNC a390 2b 0 std::_List_base<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::get_allocator() const
+a390 10 322 26
+a3a0 1b 324 66
+a3bb 1 324 66
+FUNC a3bc 7b 0 std::_List_base<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_clear()
+a3bc d 69 27
+a3c9 8 72 77
+a3d1 2 73 77
+a3d3 6 75 77
+a3d9 8 76 77
+a3e1 35 77 77
+a416 12 78 77
+a428 a 73 77
+a432 5 78 77
+a437 1 78 77
+a438 c 331 66
+a444 18 332 66
+a45c c 392 66
+a468 d 392 66
+a475 1 392 66
+a476 c 211 74
+a482 10 211 74
+a49e d 107 68
+a4ab 1 107 68
+FUNC a4ac 2e 0 void std::_Destroy<dwarf2reader::CompilationUnit::Abbrev*, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+a4ac c 171 27
+a4b8 2 173 73
+a4ba 12 174 73
+a4cc c 173 73
+a4d8 2 174 73
+a4da c 272 71
+a4e6 4b 273 71
+a531 1 273 71
+a532 13 196 74
+a545 10 196 74
+a555 2f 197 74
+a584 1a 198 74
+a59e 13 196 74
+a5b1 10 196 74
+a5c1 2f 197 74
+a5f0 1a 198 74
+a616 7 98 68
+a61d 1 98 68
+a62a 1d 85 68
+a647 5 86 68
+a64c 10 88 68
+FUNC a65c 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::_M_allocate(unsigned long)
+a65c c 116 27
+a668 1e 117 71
+a686 d 100 71
+a693 12 101 71
+a6a5 19 103 71
+a6be b 104 71
+a6c9 3a 105 71
+a703 1 105 71
+a710 7 98 68
+a717 1 98 68
+a724 1d 85 68
+a741 5 86 68
+a746 10 88 68
+FUNC a756 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::_M_allocate(unsigned long)
+a756 c 116 27
+a762 1e 117 71
+a780 d 100 71
+a78d 12 101 71
+a79f 19 103 71
+a7b8 b 104 71
+a7c3 3a 105 71
+a7fd 1 105 71
+a80b 12 424 61
+a81d 2e 425 61
+a84b 13 426 61
+a86a 4 440 61
+a86e 1b 443 61
+a889 1 443 61
+a898 56 482 61
+a8fa 4 514 61
+a8fe 4 515 61
+a902 1b 517 61
+a91d 1 517 61
+a92a 8 616 61
+a932 2 617 61
+a934 8 618 61
+a93c f 617 61
+a94b 5 619 61
+a95c 4 641 61
+a960 1b 642 61
+a97b 1 642 61
+FUNC a97c 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&, __true_type)
+a97c c 182 28
+a988 1b 183 79
+a9a3 1 183 79
+FUNC a9a4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+a9a4 c 214 28
+a9b0 23 218 79
+a9d3 1 218 79
+FUNC a9d4 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>)
+a9d4 c 308 28
+a9e0 1b 310 79
+a9fb 1 310 79
+a9fc c 200 71
+aa08 19 201 71
+aa21 42 203 71
+aa63 15 205 71
+aa85 11 992 34
+aa96 c 993 34
+aaa2 15 995 34
+aab7 c 996 34
+aac3 4a 998 34
+ab0d f 1001 34
+ab1c 1c 998 34
+ab38 1a 1003 34
+ab52 5 1004 34
+ab57 1f 1007 34
+ab76 1c 1008 34
+ab92 19 1009 34
+abab 19 1010 34
+abc4 1a 1011 34
+abde a 1004 34
+abe8 11 1001 34
+abf9 15 1014 34
+ac0e 13 1028 34
+ac21 b 1016 34
+ac2c 9 1018 34
+ac35 19 1023 34
+ac4e 23 1024 34
+ac71 19 1025 34
+ac8a 1d 1021 34
+aca7 1a 1018 34
+acc1 b 1028 34
+accc b 1016 34
+acd7 1e 1028 34
+acf5 1 1028 34
+ad06 16 438 34
+ad1c 37 439 34
+ad53 1 439 34
+ad64 37 212 42
+ad9b 1 212 42
+ada8 8 616 61
+adb0 2 617 61
+adb2 8 618 61
+adba f 617 61
+adc9 5 619 61
+adda 4 641 61
+adde 1b 642 61
+adf9 1 642 61
+FUNC adfa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&, __true_type)
+adfa c 182 28
+ae06 1b 183 79
+ae21 1 183 79
+FUNC ae22 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+ae22 c 214 28
+ae2e 23 218 79
+ae51 1 218 79
+FUNC ae52 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>)
+ae52 c 308 28
+ae5e 1b 310 79
+ae79 1 310 79
+ae7a c 200 71
+ae86 19 201 71
+ae9f 42 203 71
+aee1 15 205 71
+af03 11 992 34
+af14 c 993 34
+af20 15 995 34
+af35 c 996 34
+af41 4a 998 34
+af8b f 1001 34
+af9a 1c 998 34
+afb6 1a 1003 34
+afd0 5 1004 34
+afd5 1f 1007 34
+aff4 1c 1008 34
+b010 19 1009 34
+b029 19 1010 34
+b042 1a 1011 34
+b05c a 1004 34
+b066 11 1001 34
+b077 15 1014 34
+b08c 13 1028 34
+b09f b 1016 34
+b0aa 9 1018 34
+b0b3 19 1023 34
+b0cc 23 1024 34
+b0ef 19 1025 34
+b108 1d 1021 34
+b125 1a 1018 34
+b13f b 1028 34
+b14a b 1016 34
+b155 1e 1028 34
+b173 1 1028 34
+b184 16 438 34
+b19a 37 439 34
+b1d1 1 439 34
+b1e2 37 212 42
+b219 1 212 42
+b227 12 424 61
+b239 2e 425 61
+b267 13 426 61
+b286 4 440 61
+b28a 1b 443 61
+b2a5 1 443 61
+b2b4 56 482 61
+b316 4 514 61
+b31a 4 515 61
+b31e 1b 517 61
+b339 1 517 61
+b346 8 616 61
+b34e 2 617 61
+b350 12 618 61
+b362 16 617 61
+b378 5 619 61
+b37d 1 619 61
+b38a 4 641 61
+b38e 1b 642 61
+b3a9 1 642 61
+FUNC b3aa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&, __true_type)
+b3aa c 182 28
+b3b6 1b 183 79
+b3d1 1 183 79
+FUNC b3d2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+b3d2 c 214 28
+b3de 23 218 79
+b401 1 218 79
+FUNC b402 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*>)
+b402 c 308 28
+b40e 1b 310 79
+b429 1 310 79
+b436 8 616 61
+b43e 2 617 61
+b440 12 618 61
+b452 16 617 61
+b468 5 619 61
+b46d 1 619 61
+b47a 4 641 61
+b47e 1b 642 61
+b499 1 642 61
+FUNC b49a 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&, __true_type)
+b49a c 182 28
+b4a6 1b 183 79
+b4c1 1 183 79
+FUNC b4c2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+b4c2 c 214 28
+b4ce 23 218 79
+b4f1 1 218 79
+FUNC b4f2 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*>)
+b4f2 c 308 28
+b4fe 1b 310 79
+b519 1 310 79
+b526 22 300 61
+b548 11 301 61
+b559 1 301 61
+b566 4 315 61
+b56a 1b 317 61
+b585 1 317 61
+b592 1b 326 61
+b5ad 1 326 61
+b5ba 4 384 61
+b5be 4 385 61
+b5c2 1b 387 61
+b5dd 1 387 61
+b5ea 1b 74 79
+b605 1 74 79
+b612 23 113 79
+b635 1 113 79
+b642 1b 254 79
+b65d 1 254 79
+b66a 15 763 71
+b67f 40 766 71
+b6bf 3 768 71
+b6c2 2 773 71
+FUNC b6c4 124 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::reserve(unsigned long)
+b6c4 13 69 29
+b6d7 15 71 78
+b6ec e 72 78
+b6fa 19 73 78
+b713 e 75 78
+b721 28 78 78
+b749 3e 79 78
+b787 30 81 78
+b7b7 8 84 78
+b7bf 11 85 78
+b7d0 18 86 78
+b7f5 33 335 61
+b834 4 384 61
+b838 4 385 61
+b83c 1b 387 61
+b857 1 387 61
+b864 1b 74 79
+b87f 1 74 79
+b88c 23 113 79
+b8af 1 113 79
+b8bc 1b 254 79
+b8d7 1 254 79
+b8e6 56 354 61
+b948 4 384 61
+b94c 4 385 61
+b950 1b 387 61
+b96b 1 387 61
+b978 1b 74 79
+b993 1 74 79
+b9a0 23 113 79
+b9c3 1 113 79
+b9d0 1b 254 79
+b9eb 1 254 79
+FUNC b9ec 46e 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+b9ec 14 311 29
+ba00 b 313 78
+ba0b 24 315 78
+ba2f 8 318 78
+ba37 23 319 78
+ba5a 15 320 78
+ba6f c 321 78
+ba7b 51 323 78
+bacc 14 327 78
+bae0 30 328 78
+bb10 35 330 78
+bb45 48 334 78
+bb8d 17 338 78
+bba4 43 339 78
+bbe7 14 342 78
+bbfb 1e 343 78
+bc19 e 348 78
+bc27 1e 349 78
+bc45 e 350 78
+bc53 1d 353 78
+bc70 8 354 78
+bc78 e 355 78
+bc86 27 357 78
+bcad 6 358 78
+bcb3 4d 361 78
+bd00 40 365 78
+bd40 18 367 78
+bd58 4d 368 78
+bda5 3e 379 78
+bde3 30 381 78
+be13 12 384 78
+be25 13 385 78
+be38 22 386 78
+FUNC be5a 2e 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >* const&)
+be5a c 657 29
+be66 22 658 71
+be94 15 580 34
+bea9 15 581 34
+bebe 37 582 34
+bef5 c 583 34
+bf01 1 583 34
+bf02 d 335 34
+bf0f 4e 337 34
+bf5d 4d 338 34
+bfaa d 134 42
+bfb7 65 135 42
+c028 22 300 61
+c04a 11 301 61
+c05b 1 301 61
+c068 4 315 61
+c06c 1b 317 61
+c087 1 317 61
+c094 1b 326 61
+c0af 1 326 61
+c0bc 4 384 61
+c0c0 4 385 61
+c0c4 1b 387 61
+c0df 1 387 61
+c0ec 1b 74 79
+c107 1 74 79
+c114 23 113 79
+c137 1 113 79
+c144 1b 254 79
+c15f 1 254 79
+c16c 15 763 71
+c181 40 766 71
+c1c1 3 768 71
+c1c4 2 773 71
+FUNC c1c6 124 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::reserve(unsigned long)
+c1c6 13 69 29
+c1d9 15 71 78
+c1ee e 72 78
+c1fc 19 73 78
+c215 e 75 78
+c223 28 78 78
+c24b 3e 79 78
+c289 30 81 78
+c2b9 8 84 78
+c2c1 11 85 78
+c2d2 18 86 78
+c2f7 33 335 61
+c336 4 384 61
+c33a 4 385 61
+c33e 1b 387 61
+c359 1 387 61
+c366 1b 74 79
+c381 1 74 79
+c38e 23 113 79
+c3b1 1 113 79
+c3be 1b 254 79
+c3d9 1 254 79
+c3e8 56 354 61
+c44a 4 384 61
+c44e 4 385 61
+c452 1b 387 61
+c46d 1 387 61
+c47a 1b 74 79
+c495 1 74 79
+c4a2 23 113 79
+c4c5 1 113 79
+c4d2 1b 254 79
+c4ed 1 254 79
+FUNC c4ee 46e 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+c4ee 14 311 29
+c502 b 313 78
+c50d 24 315 78
+c531 8 318 78
+c539 23 319 78
+c55c 15 320 78
+c571 c 321 78
+c57d 51 323 78
+c5ce 14 327 78
+c5e2 30 328 78
+c612 35 330 78
+c647 48 334 78
+c68f 17 338 78
+c6a6 43 339 78
+c6e9 14 342 78
+c6fd 1e 343 78
+c71b e 348 78
+c729 1e 349 78
+c747 e 350 78
+c755 1d 353 78
+c772 8 354 78
+c77a e 355 78
+c788 27 357 78
+c7af 6 358 78
+c7b5 4d 361 78
+c802 40 365 78
+c842 18 367 78
+c85a 4d 368 78
+c8a7 3e 379 78
+c8e5 30 381 78
+c915 12 384 78
+c927 13 385 78
+c93a 22 386 78
+FUNC c95c 2e 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >**, std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node<std::pair<std::string const, __gnu_cxx::hash_map<std::string, std::pair<char const*, unsigned long long>, __gnu_cxx::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<char const*, unsigned long long> > >*> >* const&)
+c95c c 657 29
+c968 22 658 71
+c996 15 580 34
+c9ab 15 581 34
+c9c0 37 582 34
+c9f7 c 583 34
+ca03 1 583 34
+ca04 d 335 34
+ca11 4e 337 34
+ca5f 4d 338 34
+caac d 134 42
+cab9 65 135 42
+FUNC cb1e 44 0 dwarf2reader::CUFunctionInfoHandler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char)
+cb1e 39 135 42
+cb57 5 102 30
+cb5c 6 103 30
+FUNC cb62 41 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&)
+cb62 18 136 30
+cb7a 10 137 30
+cb8a 17 138 30
+cba1 2 139 30
+cba3 1 139 30
+FUNC cba4 2a5 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long)
+cba4 2d 144 30
+cbd1 a 145 30
+cbdb 58 146 30
+cc33 35 147 30
+cc68 32 146 30
+cc9a 2a 147 30
+ccc4 82 152 30
+cd46 18 153 30
+cd5e 1c 152 30
+cd7a 2f 153 30
+cda9 e 154 30
+cdb7 28 155 30
+cddf 12 157 30
+cdf1 2 158 30
+cdf3 12 160 30
+ce05 2 161 30
+ce07 c 163 30
+ce13 2 164 30
+ce15 2c 166 30
+ce41 8 172 30
+ce49 1 172 30
+FUNC ce4a 19c 0 dwarf2reader::CULineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int)
+ce4a 20 84 30
+ce6a 1c 85 30
+ce86 9c 87 30
+cf22 4f 89 30
+cf71 19 87 30
+cf8a 25 90 30
+cfaf 30 93 30
+cfdf 7 95 30
+FUNC cfe6 9f 0 dwarf2reader::CUFunctionInfoHandler::EndDIE(unsigned long long)
+cfe6 19 174 30
+cfff 1c 175 30
+d01b 65 177 30
+d080 5 178 30
+d085 1 178 30
+FUNC d086 164 0 dwarf2reader::CUFunctionInfoHandler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > > const&)
+d086 20 111 30
+d0a6 1c 112 30
+d0c2 c 126 30
+d0ce 23 115 30
+d0f1 26 116 30
+d117 1a 117 30
+d131 d 118 30
+d13e 1b 119 30
+d159 5f 120 30
+d1b8 c 124 30
+d1c4 1c 115 30
+d1e0 3 126 30
+d1e3 7 129 30
+FUNC d1ea 73 0 dwarf2reader::CULineInfoHandler::DefineDir(std::string const&, unsigned int)
+d1ea 13 52 30
+d1fd 45 54 30
+d242 15 55 30
+d257 6 56 30
+d25d 1 56 30
+FUNC d25e 23b 0 dwarf2reader::CULineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long)
+d25e 2c 60 30
+d28a 45 62 30
+d2cf 2f 65 30
+d2fe 24 66 30
+d322 b 68 30
+d32d e 69 30
+d33b 19 71 30
+d354 17 72 30
+d36b 93 74 30
+d3fe 64 77 30
+d462 30 79 30
+d492 7 81 30
+d499 1 81 30
+d49a 14 38 30
+d4ae 36 40 30
+d4e4 41 43 30
+d525 41 44 30
+d566 67 45 30
+d5cd 10 46 30
+d5dd 13 45 30
+d5f0 15 47 30
+d605 e 48 30
+d613 3d 49 30
+d650 20 50 30
+d670 14 38 30
+d684 36 40 30
+d6ba 41 43 30
+d6fb 41 44 30
+d73c 67 45 30
+d7a3 10 46 30
+d7b3 13 45 30
+d7c6 15 47 30
+d7db e 48 30
+d7e9 3d 49 30
+d826 20 50 30
+d846 12 125 74
+d858 12 125 74
+d86a 13 55 32
+d87d 35 55 32
+d8b2 13 98 32
+d8c5 35 98 32
+d8fa c 35 32
+d906 d 35 32
+d913 1 35 32
+d914 d 22 32
+d921 40 22 32
+d961 1 22 32
+d962 c 89 70
+d96e 1e 90 70
+d998 14 208 34
+d9ac c 190 67
+d9b8 a 190 67
+d9c2 c 259 67
+d9ce 21 259 67
+d9ef 1 259 67
+FUNC d9f0 13 0 std::auto_ptr<dwarf2reader::LineInfo>::operator->() const
+d9f0 c 283 35
+d9fc 7 286 67
+da03 1 286 67
+da11 5c 104 68
+da6d 1 104 68
+FUNC da6e 28 0 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
+da6e c 2139 37
+da7a 1c 2140 37
+FUNC da96 5d 0 std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
+da96 d 2081 37
+daa3 12 2083 37
+dab5 1a 2084 37
+dacf 24 2085 37
+daf3 1 2085 37
+FUNC daf4 5d 0 std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
+daf4 d 2044 37
+db01 12 2046 37
+db13 1a 2047 37
+db2d 24 2048 37
+db51 1 2048 37
+db52 c 84 70
+db5e 17 85 70
+db75 1 85 70
+FUNC db76 2d 0 std::pair<char const*, unsigned int> std::make_pair<char const*, unsigned int>(char const*, unsigned int)
+db76 c 144 37
+db82 21 145 70
+dba3 1 145 70
+dba4 c 84 70
+dbb0 23 85 70
+dbd3 1 85 70
+FUNC dbd4 3c 0 std::pair<unsigned long long, std::pair<char const*, unsigned int> > std::make_pair<unsigned long long, std::pair<char const*, unsigned int> >(unsigned long long, std::pair<char const*, unsigned int>)
+dbd4 1c 144 37
+dbf0 20 145 70
+dc10 d 89 70
+dc1d 64 90 70
+dc81 1 90 70
+dc82 c 89 70
+dc8e 2a 90 70
+dcb8 c 84 70
+dcc4 1d 85 70
+dce1 1 85 70
+FUNC dce2 3c 0 std::pair<unsigned long long, dwarf2reader::FunctionInfo*> std::make_pair<unsigned long long, dwarf2reader::FunctionInfo*>(unsigned long long, dwarf2reader::FunctionInfo*)
+dce2 1c 144 37
+dcfe 20 145 70
+dd2a a 190 34
+dd40 d 194 34
+dd4d 1 194 34
+dd4e c 603 72
+dd5a c 603 72
+FUNC dd66 2b 0 std::vector<std::string, std::allocator<std::string> >::begin() const
+dd66 c 342 39
+dd72 1f 343 71
+dd91 1 343 71
+FUNC dd92 2c 0 std::vector<std::string, std::allocator<std::string> >::end() const
+dd92 c 360 39
+dd9e 20 361 71
+ddca 5 666 72
+ddcf 1 666 72
+dddd 2b 759 72
+FUNC de08 3c 0 std::vector<std::string, std::allocator<std::string> >::size() const
+de08 c 402 39
+de14 30 403 71
+FUNC de44 2b 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::begin() const
+de44 c 342 39
+de50 1f 343 71
+de6f 1 343 71
+FUNC de70 2c 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::end() const
+de70 c 360 39
+de7c 20 361 71
+dea9 31 759 72
+FUNC deda 3c 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::size() const
+deda c 402 39
+dee6 30 403 71
+df16 c 603 72
+df22 c 603 72
+FUNC df2e 26 0 std::vector<std::string, std::allocator<std::string> >::end()
+df2e c 351 39
+df3a 1a 352 71
+df60 7 614 72
+df67 1 614 72
+FUNC df68 13 0 std::vector<std::string, std::allocator<std::string> >::max_size() const
+df68 c 407 39
+df74 7 408 71
+df7b 1 408 71
+df88 5 666 72
+df8d 1 666 72
+df9a d 623 72
+dfa7 5 624 72
+FUNC dfac 23 0 std::vector<std::string, std::allocator<std::string> >::begin()
+dfac c 333 39
+dfb8 17 334 71
+dfcf 1 334 71
+dfd0 c 35 32
+dfdc 26 35 32
+e00f 5c 104 68
+e06b 1 104 68
+e078 7 614 72
+e07f 1 614 72
+FUNC e080 35 0 dwarf2reader::SourceFileInfo::operator=(dwarf2reader::SourceFileInfo const&)
+e080 c 35 39
+e08c 29 35 32
+e0b5 1 35 32
+FUNC e0b6 13 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::max_size() const
+e0b6 c 407 39
+e0c2 7 408 71
+e0c9 1 408 71
+e0d6 d 623 72
+e0e3 5 624 72
+FUNC e0e8 3c 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::_M_range_check(unsigned long) const
+e0e8 13 515 39
+e0fb 15 517 71
+e110 14 518 71
+FUNC e124 3c 0 std::vector<std::string, std::allocator<std::string> >::_M_range_check(unsigned long) const
+e124 13 515 39
+e137 15 517 71
+e14c 14 518 71
+e16c 2a 654 72
+FUNC e196 42 0 std::vector<std::string, std::allocator<std::string> >::operator[](unsigned long)
+e196 c 494 39
+e1a2 36 495 71
+FUNC e1d8 32 0 std::vector<std::string, std::allocator<std::string> >::at(unsigned long)
+e1d8 c 534 39
+e1e4 12 536 71
+e1f6 14 537 71
+e216 32 654 72
+FUNC e248 42 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::operator[](unsigned long)
+e248 c 494 39
+e254 36 495 71
+FUNC e28a 32 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::at(unsigned long)
+e28a c 534 39
+e296 12 536 71
+e2a8 14 537 71
+FUNC e2bc 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_end()
+e2bc c 472 40
+e2c8 8 473 40
+FUNC e2d0 11 0 std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator()(std::pair<unsigned long long const, std::pair<std::string, unsigned int> > const&) const
+e2d0 c 550 41
+e2dc 5 551 41
+e2e1 1 551 41
+FUNC e2e2 53 0 std::less<unsigned long long>::operator()(unsigned long long const&, unsigned long long const&) const
+e2e2 c 226 41
+e2ee 47 227 41
+e335 1 227 41
+FUNC e336 20 0 std::_Rb_tree_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator==(std::_Rb_tree_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > const&) const
+e336 c 209 41
+e342 14 210 40
+e356 c 84 70
+e362 18 85 70
+FUNC e37a 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_end()
+e37a c 472 41
+e386 8 473 40
+FUNC e38e 11 0 std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator()(std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> const&) const
+e38e c 550 41
+e39a 5 551 41
+e39f 1 551 41
+FUNC e3a0 20 0 std::_Rb_tree_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator==(std::_Rb_tree_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > const&) const
+e3a0 c 209 41
+e3ac 14 210 40
+e3c0 c 84 70
+e3cc 18 85 70
+e3e4 c 180 34
+e3f0 13 181 34
+e403 1 181 34
+e410 22 409 34
+e43e d 207 42
+e44b 1 207 42
+FUNC e44c 35 0 bool __gnu_cxx::operator!=<std::string*, std::vector<std::string, std::allocator<std::string> > >(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > > const&, __gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > > const&)
+e44c d 699 42
+e459 28 700 72
+e481 1 700 72
+FUNC e482 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, std::allocator<std::string> >(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, __gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, std::allocator<std::string>)
+e482 c 171 43
+e48e 2 173 73
+e490 1a 174 73
+e4aa 21 173 73
+e4cb 2 174 73
+e4cd 1 174 73
+FUNC e4ce 35 0 bool __gnu_cxx::operator!=<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > const&, __gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > const&)
+e4ce d 699 43
+e4db 28 700 72
+e503 1 700 72
+FUNC e504 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, std::allocator<dwarf2reader::SourceFileInfo> >(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, __gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, std::allocator<dwarf2reader::SourceFileInfo>)
+e504 c 171 43
+e510 2 173 73
+e512 1a 174 73
+e52c 21 173 73
+e54d 2 174 73
+e54f 1 174 73
+FUNC e550 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_value(std::_Rb_tree_node<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > const*)
+e550 c 480 43
+e55c 8 481 40
+FUNC e564 28 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_key(std::_Rb_tree_node<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > const*)
+e564 c 484 43
+e570 1c 485 40
+FUNC e58c 25 0 std::_Rb_tree_iterator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >::operator--()
+e58c c 194 43
+e598 14 196 40
+e5ac 5 197 40
+e5b1 1 197 40
+FUNC e5b2 25 0 std::_Rb_tree_iterator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >::operator--()
+e5b2 c 194 43
+e5be 14 196 40
+e5d2 5 197 40
+e5d7 1 197 40
+FUNC e5d8 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_value(std::_Rb_tree_node_base const*)
+e5d8 c 504 43
+e5e4 8 505 40
+FUNC e5ec 28 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_S_key(std::_Rb_tree_node_base const*)
+e5ec c 508 43
+e5f8 1c 509 40
+FUNC e614 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_value(std::_Rb_tree_node<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > const*)
+e614 c 480 43
+e620 8 481 40
+FUNC e628 28 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_key(std::_Rb_tree_node<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > const*)
+e628 c 484 43
+e634 1c 485 40
+FUNC e650 14 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_value(std::_Rb_tree_node_base const*)
+e650 c 504 43
+e65c 8 505 40
+FUNC e664 28 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_S_key(std::_Rb_tree_node_base const*)
+e664 c 508 43
+e670 1c 509 40
+e698 7 614 72
+e69f 1 614 72
+e6ac 7 98 68
+e6b3 1 98 68
+e6c0 1d 85 68
+e6dd 5 86 68
+e6e2 10 88 68
+FUNC e6f2 2a 0 std::_Vector_base<std::string, std::allocator<std::string> >::_M_allocate(unsigned long)
+e6f2 c 116 43
+e6fe 1e 117 71
+e728 7 98 68
+e72f 1 98 68
+e73c 1d 85 68
+e759 5 86 68
+e75e 16 88 68
+FUNC e774 2a 0 std::_Vector_base<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::_M_allocate(unsigned long)
+e774 c 116 43
+e780 1e 117 71
+e7aa 3a 104 68
+e7f0 2a 654 72
+FUNC e81a 42 0 std::vector<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*, std::allocator<__gnu_cxx::_Hashtable_node<std::pair<std::string const, std::pair<char const*, unsigned long long> > >*> >::operator[](unsigned long) const
+e81a c 509 43
+e826 36 510 71
+FUNC e85c 4e 0 std::string* std::__copy_backward<false, std::random_access_iterator_tag>::copy_b<std::string*, std::string*>(std::string*, std::string*, std::string*)
+e85c c 408 61
+e868 14 411 61
+e87c 1e 412 61
+e89a b 411 61
+e8a5 5 413 61
+FUNC e8aa 2b 0 std::string* std::__copy_backward_aux<std::string*, std::string*>(std::string*, std::string*, std::string*)
+e8aa c 432 44
+e8b6 4 440 61
+e8ba 1b 443 61
+e8d5 1 443 61
+e8e4 56 482 61
+e946 4 514 61
+e94a 4 515 61
+e94e 1b 517 61
+e969 1 517 61
+FUNC e96a 69 0 void std::_Construct<std::string, std::string>(std::string*, std::string const&)
+e96a d 77 44
+e977 5c 81 73
+e9d3 1 81 73
+FUNC e9d4 54 0 dwarf2reader::SourceFileInfo* std::__copy_backward<false, std::random_access_iterator_tag>::copy_b<dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*>(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*)
+e9d4 c 408 61
+e9e0 1a 411 61
+e9fa 1e 412 61
+ea18 b 411 61
+ea23 5 413 61
+FUNC ea28 2b 0 dwarf2reader::SourceFileInfo* std::__copy_backward_aux<dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*>(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*)
+ea28 c 432 44
+ea34 4 440 61
+ea38 1b 443 61
+ea53 1 443 61
+ea62 56 482 61
+eac4 4 514 61
+eac8 4 515 61
+eacc 1b 517 61
+eae7 1 517 61
+FUNC eae8 69 0 void std::_Construct<dwarf2reader::SourceFileInfo, dwarf2reader::SourceFileInfo>(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo const&)
+eae8 d 77 44
+eaf5 5c 81 73
+eb51 1 81 73
+eb52 c 69 70
+eb5e 20 69 70
+eb7e c 69 70
+eb8a 2a 69 70
+ebc1 5c 104 68
+ec1d 1 104 68
+ec2a 15 523 34
+ec3f 79 525 34
+ecb8 21 529 34
+ecd9 1 529 34
+ece6 14 229 42
+ed06 7 98 68
+ed0d 1 98 68
+ed1a 1d 85 68
+ed37 5 86 68
+ed3c 10 88 68
+FUNC ed4c 29 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_get_node()
+ed4c c 355 44
+ed58 1d 356 40
+ed75 1 356 40
+FUNC ed76 b6 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_create_node(std::pair<unsigned long long const, std::pair<std::string, unsigned int> > const&)
+ed76 d 363 44
+ed83 e 365 40
+ed91 3c 367 40
+edcd b 373 40
+edd8 11 367 40
+ede9 b 368 40
+edf4 12 370 40
+ee06 b 371 40
+ee11 13 368 40
+ee24 8 373 40
+FUNC ee2c cd 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair<unsigned long long const, std::pair<std::string, unsigned int> > const&)
+ee2c d 787 44
+ee39 15 789 40
+ee4e 5d 792 40
+eeab 24 796 40
+eecf f 798 40
+eede 1b 799 40
+eef9 1 799 40
+FUNC eefa 1ef 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, std::pair<std::string, unsigned int> >, std::_Select1st<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::insert_unique(std::pair<unsigned long long const, std::pair<std::string, unsigned int> > const&)
+eefa d 869 44
+ef07 e 871 40
+ef15 e 872 40
+ef23 4 873 40
+ef27 2 874 40
+ef29 6 876 40
+ef2f 35 877 40
+ef64 2a 878 40
+ef8e 6 874 40
+ef94 12 880 40
+efa6 a 881 40
+efb0 24 882 40
+efd4 51 883 40
+f025 b 885 40
+f030 36 886 40
+f066 4e 887 40
+f0b4 35 888 40
+f0e9 1 888 40
+FUNC f0ea 20 0 std::map<unsigned long long, std::pair<std::string, unsigned int>, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, std::pair<std::string, unsigned int> > > >::insert(std::pair<unsigned long long const, std::pair<std::string, unsigned int> > const&)
+f0ea c 359 45
+f0f6 14 360 45
+f116 7 98 68
+f11d 1 98 68
+f12a 1d 85 68
+f147 5 86 68
+f14c 1d 88 68
+f169 1 88 68
+FUNC f16a 29 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_get_node()
+f16a c 355 45
+f176 1d 356 40
+f193 1 356 40
+FUNC f194 5f 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_create_node(std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> const&)
+f194 d 363 45
+f1a1 e 365 40
+f1af 3c 367 40
+f1eb 8 373 40
+f1f3 1 373 40
+FUNC f1f4 cd 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> const&)
+f1f4 d 787 45
+f201 15 789 40
+f216 5d 792 40
+f273 24 796 40
+f297 f 798 40
+f2a6 1b 799 40
+f2c1 1 799 40
+FUNC f2c2 1ef 0 std::_Rb_tree<unsigned long long, std::pair<unsigned long long const, dwarf2reader::FunctionInfo*>, std::_Select1st<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> >, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::insert_unique(std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> const&)
+f2c2 d 869 45
+f2cf e 871 40
+f2dd e 872 40
+f2eb 4 873 40
+f2ef 2 874 40
+f2f1 6 876 40
+f2f7 35 877 40
+f32c 2a 878 40
+f356 6 874 40
+f35c 12 880 40
+f36e a 881 40
+f378 24 882 40
+f39c 51 883 40
+f3ed b 885 40
+f3f8 36 886 40
+f42e 4e 887 40
+f47c 35 888 40
+f4b1 1 888 40
+FUNC f4b2 20 0 std::map<unsigned long long, dwarf2reader::FunctionInfo*, std::less<unsigned long long>, std::allocator<std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> > >::insert(std::pair<unsigned long long const, dwarf2reader::FunctionInfo*> const&)
+f4b2 c 359 45
+f4be 14 360 45
+FUNC f4d2 19 0 void std::_Destroy<std::string>(std::string*)
+f4d2 c 106 45
+f4de d 107 73
+f4eb 1 107 73
+FUNC f4ec 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > > >(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, __gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, __false_type)
+f4ec c 119 45
+f4f8 2 121 73
+f4fa 13 122 73
+f50d 21 121 73
+f52e 2 122 73
+FUNC f530 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > > >(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, __gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >)
+f530 c 148 45
+f53c 1c 155 73
+f565 6 82 79
+f56b 2 85 79
+f56d 24 86 79
+f591 2c 85 79
+f5bd b 87 79
+f5c8 b 89 79
+f5d3 12 91 79
+f5e5 b 92 79
+f5f0 13 89 79
+f603 9 92 79
+f618 23 113 79
+f63b 1 113 79
+f648 1b 254 79
+f663 1 254 79
+FUNC f664 430 0 std::vector<std::string, std::allocator<std::string> >::_M_insert_aux(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >, std::string const&)
+f664 14 249 47
+f678 14 251 78
+f68c 22 253 78
+f6ae f 255 78
+f6bd 12 256 78
+f6cf 55 257 78
+f724 4b 260 78
+f76f e 264 78
+f77d 15 265 78
+f792 e 266 78
+f7a0 1d 271 78
+f7bd 8 272 78
+f7c5 e 273 78
+f7d3 27 275 78
+f7fa 6 276 78
+f800 55 279 78
+f855 25 284 78
+f87a b 285 78
+f885 4f 286 78
+f8d4 3 284 78
+f8d7 13 279 78
+f8ea e 286 78
+f8f8 4d 298 78
+f945 30 299 78
+f975 12 302 78
+f987 13 303 78
+f99a 23 304 78
+f9bd 3 298 78
+f9c0 13 286 78
+f9d3 b 292 78
+f9de 39 294 78
+fa17 23 295 78
+fa3a 8 296 78
+fa42 16 294 78
+fa58 3 296 78
+fa5b 19 292 78
+fa74 19 298 78
+fa8d 7 304 78
+FUNC fa94 70 0 std::vector<std::string, std::allocator<std::string> >::push_back(std::string const&)
+fa94 c 602 47
+faa0 10 604 71
+fab0 1e 606 71
+face 11 607 71
+fadf 25 610 71
+FUNC fb04 19 0 void std::_Destroy<dwarf2reader::SourceFileInfo>(dwarf2reader::SourceFileInfo*)
+fb04 c 106 47
+fb10 d 107 73
+fb1d 1 107 73
+FUNC fb1e 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > >(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, __gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, __false_type)
+fb1e c 119 47
+fb2a 2 121 73
+fb2c 13 122 73
+fb3f 21 121 73
+fb60 2 122 73
+FUNC fb62 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > > >(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, __gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >)
+fb62 c 148 47
+fb6e 1c 155 73
+fb97 6 82 79
+fb9d 2 85 79
+fb9f 24 86 79
+fbc3 2c 85 79
+fbef b 87 79
+fbfa b 89 79
+fc05 12 91 79
+fc17 b 92 79
+fc22 13 89 79
+fc35 9 92 79
+fc4a 23 113 79
+fc6d 1 113 79
+fc7a 1b 254 79
+fc95 1 254 79
+FUNC fc96 43d 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::_M_insert_aux(__gnu_cxx::__normal_iterator<dwarf2reader::SourceFileInfo*, std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> > >, dwarf2reader::SourceFileInfo const&)
+fc96 14 249 47
+fcaa 14 251 78
+fcbe 22 253 78
+fce0 f 255 78
+fcef 12 256 78
+fd01 55 257 78
+fd56 4b 260 78
+fda1 e 264 78
+fdaf 15 265 78
+fdc4 e 266 78
+fdd2 1d 271 78
+fdef 8 272 78
+fdf7 e 273 78
+fe05 27 275 78
+fe2c 6 276 78
+fe32 55 279 78
+fe87 25 284 78
+feac b 285 78
+feb7 4f 286 78
+ff06 3 284 78
+ff09 13 279 78
+ff1c e 286 78
+ff2a 4d 298 78
+ff77 36 299 78
+ffad 12 302 78
+ffbf 13 303 78
+ffd2 2a 304 78
+fffc 3 298 78
+ffff 13 286 78
+10012 b 292 78
+1001d 39 294 78
+10056 23 295 78
+10079 8 296 78
+10081 16 294 78
+10097 3 296 78
+1009a 19 292 78
+100b3 19 298 78
+100cc 7 304 78
+100d3 1 304 78
+FUNC 100d4 70 0 std::vector<dwarf2reader::SourceFileInfo, std::allocator<dwarf2reader::SourceFileInfo> >::push_back(dwarf2reader::SourceFileInfo const&)
+100d4 c 602 47
+100e0 10 604 71
+100f0 1e 606 71
+1010e 11 607 71
+1011f 25 610 71
+FUNC 10144 16c 0 Start
+10144 17 610 71
+1015b 40 49 48
+1019b 6 51 48
+101a1 3f 53 48
+101e0 7 54 48
+101e7 5 55 48
+101ec 2a 58 48
+10216 61 61 48
+10277 7 62 48
+1027e 2 63 48
+10280 29 66 48
+102a9 7 67 48
+FUNC 102b0 108 0 Usage
+102b0 19 70 48
+102c9 30 71 48
+102f9 29 73 48
+10322 30 74 48
+10352 30 75 48
+10382 30 76 48
+103b2 6 77 48
+FUNC 103b8 3af 0 SetupOptions
+103b8 21 80 48
+103d9 8 82 48
+103e1 6 85 48
+103e7 10 86 48
+103f7 2e 88 48
+10425 2f 94 48
+10454 2a 91 48
+1047e 23 95 48
+104a1 3e 97 48
+104df 11 98 48
+104f0 7c 99 48
+1056c c 100 48
+10578 5 99 48
+1057d 3e 101 48
+105bb 11 102 48
+105cc 3e 103 48
+1060a 11 104 48
+1061b 37 106 48
+10652 b 107 48
+1065d c 108 48
+10669 b 113 48
+10674 c 114 48
+10680 14 119 48
+10694 30 120 48
+106c4 b 121 48
+106cf c 122 48
+106db 81 127 48
+1075c b 128 48
+10767 1 128 48
+FUNC 10768 a7 0 main
+10768 13 131 48
+1077b 37 132 48
+107b2 1e 135 48
+107d0 e 136 48
+107de 8 137 48
+107e6 17 139 48
+107fd c 141 48
+10809 6 142 48
+1080f 1 142 48
+10810 c 47 49
+1081c 1a 48 49
+10836 2 49 49
+10838 c 47 49
+10844 1a 48 49
+1085e 2 49 49
+FUNC 10860 cb 0 google_breakpad::FileID::FileIdentifier(unsigned char*)
+10860 f 51 49
+1086f 16 52 49
+10885 6 53 49
+1088b f 54 49
+1089a b 57 49
+108a5 7 62 49
+108ac 2 63 49
+108ae 1c 64 49
+108ca 32 63 49
+108fc b 67 49
+10907 12 68 49
+10919 10 70 49
+10929 2 71 49
+1092b 1 71 49
+FUNC 1092c f2 0 google_breakpad::FileID::MachoIdentifier(int, unsigned char*)
+1092c 10 73 49
+1093c 15 74 49
+10951 20 76 49
+10971 f 77 49
+10980 20 79 49
+109a0 c 80 49
+109ac 69 82 49
+10a15 9 83 49
+FUNC 10a1e fb 0 google_breakpad::FileID::ConvertIdentifierToString(unsigned char const*, char*, int)
+10a1e c 87 49
+10a2a 7 88 49
+10a31 c 89 49
+10a3d 15 90 49
+10a52 12 91 49
+10a64 18 93 49
+10a7c e 94 49
+10a8a 2b 96 49
+10ab5 2b 97 49
+10ae0 17 89 49
+10af7 20 101 49
+10b17 2 102 49
+10b19 1 102 49
+FUNC 10b1a 13 0 NXHostByteOrder
+10b1a c 144 56
+10b26 5 147 56
+10b2b 2 153 56
+10b2d 1 153 56
+10b2e c 56 51
+10b3a 1a 57 51
+10b54 1e 58 51
+10b72 2 59 51
+10b74 c 56 51
+10b80 1a 57 51
+10b9a 1e 58 51
+10bb8 2 59 51
+10bba c 61 51
+10bc6 e 62 51
+10bd4 11 63 51
+10be5 2 64 51
+10be7 1 64 51
+10be8 c 61 51
+10bf4 e 62 51
+10c02 11 63 51
+10c13 2 64 51
+10c15 1 64 51
+FUNC 10c16 477 0 MacFileUtilities::MachoID::UpdateCRC(unsigned char*, unsigned long)
+10c16 c 74 51
+10c22 11 82 51
+10c33 14 83 51
+10c47 5 86 51
+10c4c 9 87 51
+10c55 7 88 51
+10c5c 18b 90 51
+10de7 6 91 51
+10ded 14 89 51
+10e01 23 93 51
+10e24 23 94 51
+10e47 d 86 51
+10e54 f 98 51
+10e63 6 100 51
+10e69 18b 101 51
+10ff4 6 102 51
+10ffa c 99 51
+11006 13 105 51
+11019 8 106 51
+11021 10 104 51
+11031 23 108 51
+11054 23 109 51
+11077 14 110 51
+1108b 2 112 51
+1108d 1 112 51
+FUNC 1108e 2c 0 MacFileUtilities::MachoID::UpdateMD5(unsigned char*, unsigned long)
+1108e c 114 51
+1109a 1e 115 51
+110b8 2 116 51
+FUNC 110ba 2c 0 MacFileUtilities::MachoID::UpdateSHA1(unsigned char*, unsigned long)
+110ba c 118 51
+110c6 1e 119 51
+110e4 2 120 51
+FUNC 110e6 121 0 MacFileUtilities::MachoID::Update(MacFileUtilities::MachoWalker*, unsigned long, unsigned long)
+110e6 f 122 51
+110f5 1b 123 51
+11110 e 129 51
+1111e 5 130 51
+11123 9 131 51
+1112c 7 132 51
+11133 a 133 51
+1113d 6 135 51
+11143 7 136 51
+1114a 35 139 51
+1117f 6c 142 51
+111eb 10 143 51
+111fb a 130 51
+11205 2 145 51
+11207 1 145 51
+FUNC 11208 cf 0 MacFileUtilities::MachoID::UUIDCommand(int, unsigned char*)
+11208 14 147 51
+1121c 25 149 51
+11241 7 151 51
+11248 19 152 51
+11261 9 153 51
+1126a 8 157 51
+11272 1f 158 51
+11291 9 159 51
+1129a 36 162 51
+112d0 7 163 51
+112d7 1 163 51
+FUNC 112d8 224 0 MacFileUtilities::MachoID::IDCommand(int, unsigned char*)
+112d8 15 165 51
+112ed 25 167 51
+11312 7 169 51
+11319 19 170 51
+11332 c 171 51
+1133e c 175 51
+1134a 6 180 51
+11350 7 181 51
+11357 9 182 51
+11360 9 183 51
+11369 28 185 51
+11391 33 186 51
+113c4 1e 185 51
+113e2 10 189 51
+113f2 10 190 51
+11402 10 191 51
+11412 d 192 51
+1141f 10 193 51
+1142f 10 194 51
+1143f 10 195 51
+1144f d 196 51
+1145c 17 197 51
+11473 17 198 51
+1148a 17 199 51
+114a1 14 200 51
+114b5 9 202 51
+114be 36 205 51
+114f4 8 206 51
+FUNC 114fc d1 0 MacFileUtilities::MachoID::Adler32(int)
+114fc 14 208 51
+11510 25 209 51
+11535 27 210 51
+1155c d 211 51
+11569 19 213 51
+11582 9 214 51
+1158b 3b 216 51
+115c6 7 217 51
+115cd 1 217 51
+FUNC 115ce f8 0 MacFileUtilities::MachoID::MD5(int, unsigned char*)
+115ce 14 219 51
+115e2 25 220 51
+11607 27 221 51
+1162e 19 223 51
+11647 19 224 51
+11660 9 225 51
+11669 17 227 51
+11680 9 228 51
+11689 36 231 51
+116bf 7 232 51
+FUNC 116c6 f8 0 MacFileUtilities::MachoID::SHA1(int, unsigned char*)
+116c6 14 234 51
+116da 25 235 51
+116ff 27 236 51
+11726 19 238 51
+1173f 19 239 51
+11758 9 240 51
+11761 17 242 51
+11778 9 243 51
+11781 36 246 51
+117b7 7 247 51
+FUNC 117be 378 0 MacFileUtilities::MachoID::WalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*)
+117be 2b 251 51
+117e9 6 252 51
+117ef e 254 51
+117fd 38 257 51
+11835 f 258 51
+11844 9 260 51
+1184d 17 261 51
+11864 20 266 51
+11884 f 267 51
+11893 d 271 51
+118a0 c 273 51
+118ac 38 274 51
+118e4 f 275 51
+118f3 9 277 51
+118fc 1f 278 51
+1191b 14 282 51
+1192f 2b 283 51
+1195a d 285 51
+11967 19 273 51
+11980 e 287 51
+1198e 38 290 51
+119c6 f 291 51
+119d5 9 293 51
+119de 17 294 51
+119f5 20 299 51
+11a15 f 300 51
+11a24 d 304 51
+11a31 c 306 51
+11a3d 38 307 51
+11a75 f 308 51
+11a84 9 310 51
+11a8d 1f 311 51
+11aac 1a 315 51
+11ac6 39 316 51
+11aff d 318 51
+11b0c 11 306 51
+11b1d 10 323 51
+11b2d 9 324 51
+FUNC 11b36 95 0 MacFileUtilities::MachoID::UUIDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*)
+11b36 1e 328 51
+11b54 a 329 51
+11b5e 6 331 51
+11b64 2f 333 51
+11b93 9 335 51
+11b9c 6 337 51
+11ba2 14 338 51
+11bb6 9 340 51
+11bbf a 344 51
+11bc9 2 345 51
+11bcb 1 345 51
+FUNC 11bcc 95 0 MacFileUtilities::MachoID::IDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*)
+11bcc 1e 349 51
+11bea a 350 51
+11bf4 6 351 51
+11bfa 2f 353 51
+11c29 9 354 51
+11c32 6 356 51
+11c38 14 357 51
+11c4c 9 359 51
+11c55 a 363 51
+11c5f 2 364 51
+11c61 1 364 51
+FUNC 11c62 1c 0 _OSSwapInt32
+11c62 f 53 55
+11c71 8 55 55
+11c79 3 56 55
+11c7c 2 57 55
+FUNC 11c7e 19 0 NXSwapInt
+11c7e f 52 56
+11c8d 8 54 56
+11c95 2 55 56
+11c97 1 55 56
+FUNC 11c98 13 0 NXHostByteOrder
+11c98 c 144 56
+11ca4 5 147 56
+11ca9 2 153 56
+11cab 1 153 56
+11cac c 52 54
+11cb8 12 54 54
+11cca 1a 55 54
+11ce4 2 56 54
+11ce6 c 52 54
+11cf2 12 54 54
+11d04 1a 55 54
+11d1e 2 56 54
+11d20 c 58 54
+11d2c a 59 54
+11d36 d 60 54
+11d43 2 61 54
+11d45 1 61 54
+11d46 c 58 54
+11d52 a 59 54
+11d5c d 60 54
+11d69 2 61 54
+11d6b 1 61 54
+FUNC 11d6c 37 0 MacFileUtilities::MachoWalker::ValidateCPUType(int)
+11d6c c 63 54
+11d78 6 66 54
+11d7e 8 67 54
+11d86 6 68 54
+11d8c b 69 54
+11d97 7 74 54
+11d9e 3 80 54
+11da1 2 81 54
+11da3 1 81 54
+FUNC 11da4 50 0 MacFileUtilities::MachoWalker::ReadBytes(void*, unsigned long, long long)
+11da4 18 96 54
+11dbc 36 97 54
+11df2 2 98 54
+FUNC 11df4 73 0 MacFileUtilities::MachoWalker::CurrentHeader(mach_header_64*, long long*)
+11df4 c 100 54
+11e00 a 101 54
+11e0a 37 102 54
+11e41 11 103 54
+11e52 9 104 54
+11e5b a 107 54
+11e65 2 108 54
+11e67 1 108 54
+FUNC 11e68 2a6 0 MacFileUtilities::MachoWalker::FindHeader(int, long long&)
+11e68 c 110 54
+11e74 15 111 54
+11e89 31 114 54
+11eba c 115 54
+11ec6 10 117 54
+11ed6 4 120 54
+11eda 14 121 54
+11eee 4 122 54
+11ef2 11 129 54
+11f03 28 124 54
+11f2b c 126 54
+11f37 31 133 54
+11f68 c 134 54
+11f74 14 136 54
+11f88 b 137 54
+11f93 8 139 54
+11f9b c 140 54
+11fa7 10 142 54
+11fb7 c 143 54
+11fc3 10 146 54
+11fd3 31 148 54
+12004 c 149 54
+12010 f 151 54
+1201f 14 152 54
+12033 16 154 54
+12049 c 158 54
+12055 31 159 54
+12086 9 160 54
+1208f f 162 54
+1209e 1c 163 54
+120ba 8 165 54
+120c2 10 166 54
+120d2 9 167 54
+120db 16 170 54
+120f1 11 158 54
+12102 a 174 54
+1210c 2 175 54
+FUNC 1210e 109 0 MacFileUtilities::MachoWalker::WalkHeaderCore(long long, unsigned int, bool)
+1210e 1e 224 54
+1212c c 225 54
+12138 2f 227 54
+12167 c 228 54
+12173 6 230 54
+12179 14 231 54
+1218d 5b 234 54
+121e8 12 237 54
+121fa 11 225 54
+1220b a 240 54
+12215 2 241 54
+12217 1 241 54
+FUNC 12218 10e 0 MacFileUtilities::MachoWalker::WalkHeader64AtOffset(long long)
+12218 18 203 54
+12230 2f 205 54
+1225f c 206 54
+1226b e 208 54
+12279 6 209 54
+1227f 14 210 54
+12293 9 212 54
+1229c a 213 54
+122a6 f 214 54
+122b5 15 215 54
+122ca 2b 216 54
+122f5 a 217 54
+122ff a 218 54
+12309 11 219 54
+1231a a 220 54
+12324 2 221 54
+FUNC 12326 143 0 MacFileUtilities::MachoWalker::WalkHeaderAtOffset(long long)
+12326 18 177 54
+1233e 2f 179 54
+1236d c 180 54
+12379 e 182 54
+12387 6 183 54
+1238d 14 184 54
+123a1 2e 189 54
+123cf 7 190 54
+123d6 9 192 54
+123df a 193 54
+123e9 f 194 54
+123f8 15 195 54
+1240d 2b 196 54
+12438 a 197 54
+12442 a 198 54
+1244c 11 199 54
+1245d a 200 54
+12467 2 201 54
+12469 1 201 54
+FUNC 1246a 99 0 MacFileUtilities::MachoWalker::WalkHeader(int)
+1246a c 83 54
+12476 15 84 54
+1248b 1d 86 54
+124a8 d 87 54
+124b5 21 88 54
+124d6 21 90 54
+124f7 a 93 54
+12501 2 94 54
+12503 1 94 54
+FUNC 12504 1c 0 _OSSwapInt32
+12504 f 53 55
+12513 8 55 55
+1251b 3 56 55
+1251e 2 57 55
+FUNC 12520 2b 0 _OSSwapInt64
+12520 12 64 55
+12532 11 69 55
+12543 6 70 55
+12549 2 71 55
+1254b 1 71 55
+FUNC 1254c 19 0 NXSwapLong
+1254c f 61 56
+1255b 8 63 56
+12563 2 64 56
+12565 1 64 56
+FUNC 12566 1f 0 NXSwapLongLong
+12566 12 70 56
+12578 b 72 56
+12583 2 73 56
+12585 1 73 56
+FUNC 12586 32 0 breakpad_swap_uuid_command(breakpad_uuid_command*, NXByteOrder)
+12586 c 37 57
+12592 11 39 57
+125a3 13 40 57
+125b6 2 41 57
+FUNC 125b8 da 0 breakpad_swap_segment_command_64(segment_command_64*, NXByteOrder)
+125b8 c 44 57
+125c4 11 46 57
+125d5 13 47 57
+125e8 17 49 57
+125ff 17 50 57
+12616 17 51 57
+1262d 17 52 57
+12644 13 54 57
+12657 13 55 57
+1266a 13 56 57
+1267d 13 57 57
+12690 2 58 57
+FUNC 12692 a4 0 breakpad_swap_mach_header_64(mach_header_64*, NXByteOrder)
+12692 c 61 57
+1269e 11 63 57
+126af 13 64 57
+126c2 13 65 57
+126d5 13 66 57
+126e8 13 67 57
+126fb 13 68 57
+1270e 13 69 57
+12721 13 70 57
+12734 2 71 57
+FUNC 12736 1d1 0 breakpad_swap_section_64(section_64*, unsigned int, NXByteOrder)
+12736 d 75 57
+12743 c 77 57
+1274f 33 78 57
+12782 33 79 57
+127b5 2d 81 57
+127e2 2d 82 57
+1280f 2d 83 57
+1283c 2d 84 57
+12869 2d 85 57
+12896 2d 86 57
+128c3 2d 87 57
+128f0 11 77 57
+12901 6 89 57
+12907 1 89 57
+12908 12 9 58
+1291a 4f 11 58
+12969 2 12 58
+1296b 1 12 58
+1296c 12 9 58
+1297e 4f 11 58
+129cd 2 12 58
+129cf 1 12 58
+129d0 13 14 58
+129e3 2a 14 58
+12a0d 1 14 58
+12a0e 13 14 58
+12a21 2a 14 58
+12a4b 1 14 58
+12a4c 13 14 58
+12a5f 2a 14 58
+12a89 1 14 58
+FUNC 12a8a bb 0 dwarf2reader::ByteReader::SetOffsetSize(unsigned char)
+12a8a 19 16 58
+12aa3 a 17 58
+12aad 48 18 58
+12af5 6 19 58
+12afb 23 20 58
+12b1e 21 22 58
+12b3f 6 24 58
+12b45 1 24 58
+FUNC 12b46 bb 0 dwarf2reader::ByteReader::SetAddressSize(unsigned char)
+12b46 19 26 58
+12b5f a 27 58
+12b69 48 28 58
+12bb1 6 29 58
+12bb7 23 30 58
+12bda 21 32 58
+12bfb 6 34 58
+12c01 1 34 58
+FUNC 12c02 a2 0 dwarf2reader::ByteReader::ReadFourBytes(char const*) const
+12c02 c 24 59
+12c0e c 25 64
+12c1a d 26 64
+12c27 f 27 64
+12c36 f 28 64
+12c45 b 29 64
+12c50 27 30 64
+12c77 2b 32 64
+12ca2 2 34 64
+FUNC 12ca4 40e 0 dwarf2reader::ByteReader::ReadEightBytes(char const*) const
+12ca4 11 36 59
+12cb5 1a 37 64
+12ccf 1b 38 64
+12cea 1d 39 64
+12d07 1d 40 64
+12d24 1d 41 64
+12d41 1d 42 64
+12d5e 1d 43 64
+12d7b 1d 44 64
+12d98 f 45 64
+12da7 18f 47 64
+12f36 172 50 64
+130a8 a 52 64
+130b2 2 52 64
+FUNC 130b4 a6 0 ReadInitialLength
+130b4 15 29 60
+130c9 18 30 60
+130e1 6 31 60
+130e7 d 35 60
+130f4 13 36 60
+13107 9 37 60
+13110 1a 38 60
+1312a 13 40 60
+1313d 9 41 60
+13146 12 43 60
+13158 2 44 60
+1315a 1f 47 60
+13179 65 50 60
+131de 1f 47 60
+131fd 65 50 60
+FUNC 13262 393 0 dwarf2reader::CompilationUnit::SkipAttribute(char const*, dwarf2reader::DwarfForm)
+13262 14 133 60
+13276 82 136 60
+132f8 1f 139 60
+13317 a 140 60
+13321 21 141 60
+13342 c 147 60
+1334e e 151 60
+1335c e 155 60
+1336a e 159 60
+13378 27 162 60
+1339f 1c 166 60
+133bb 10 167 60
+133cb 1c 171 60
+133e7 10 172 60
+133f7 1e 175 60
+13415 56 180 60
+1346b d 181 60
+13478 1e 182 60
+13496 11 183 60
+134a7 1e 184 60
+134c5 24 189 60
+134e9 26 192 60
+1350f 23 195 60
+13532 22 198 60
+13554 15 199 60
+13569 1b 203 60
+13584 30 206 60
+135b4 30 208 60
+135e4 a 209 60
+135ee 7 210 60
+135f5 1 210 60
+FUNC 135f6 29b 0 dwarf2reader::CompilationUnit::ReadHeader()
+135f6 14 217 60
+1360a 9 218 60
+13613 4e 221 60
+13661 17 223 60
+13678 a 224 60
+13682 f 225 60
+13691 4e 227 60
+136df 1e 228 60
+136fd 6 229 60
+13703 5e 231 60
+13761 1e 232 60
+1377f 18 233 60
+13797 4c 235 60
+137e3 1d 236 60
+13800 1c 237 60
+1381c 5 238 60
+13821 9 240 60
+1382a 60 245 60
+1388a 7 247 60
+13891 1 247 60
+FUNC 13892 a57 0 dwarf2reader::CompilationUnit::ProcessAttribute(unsigned long long, char const*, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm)
+13892 24 299 60
+138b6 8a 302 60
+13940 1f 307 60
+1395f a 308 60
+13969 36 309 60
+1399f 5b 316 60
+139fa c 317 60
+13a06 5b 322 60
+13a61 e 323 60
+13a6f 55 328 60
+13ac4 e 329 60
+13ad2 55 334 60
+13b27 e 335 60
+13b35 6 338 60
+13b3b 9a 340 60
+13bd5 33 341 60
+13c08 25 340 60
+13c2d 5c 348 60
+13c89 10 349 60
+13c99 5c 354 60
+13cf5 10 355 60
+13d05 55 359 60
+13d5a 1e 360 60
+13d78 56 365 60
+13dce d 366 60
+13ddb 55 368 60
+13e30 1e 369 60
+13e4e 11 370 60
+13e5f 55 372 60
+13eb4 1e 373 60
+13ed2 29 378 60
+13efb 4a 380 60
+13f45 f 381 60
+13f54 29 385 60
+13f7d 4c 387 60
+13fc9 11 388 60
+13fda 1b 392 60
+13ff5 4c 394 60
+14041 11 395 60
+14052 22 399 60
+14074 4e 401 60
+140c2 15 402 60
+140d7 3c 406 60
+14113 1b 408 60
+1412e 54 409 60
+14182 f 411 60
+14191 9a 413 60
+1422b 24 414 60
+1424f 25 413 60
+14274 30 418 60
+142a4 30 420 60
+142d4 a 421 60
+142de b 422 60
+142e9 1 422 60
+142ea 1f 489 60
+14309 3a 491 60
+14343 a 492 60
+1434d 6 493 60
+14353 1 493 60
+14354 1f 489 60
+14373 3a 491 60
+143ad a 492 60
+143b7 6 493 60
+143bd 1 493 60
+FUNC 143be b5 0 dwarf2reader::CompilationUnit::ProcessDIE(unsigned long long, char const*, dwarf2reader::CompilationUnit::Abbrev const&)
+143be 19 426 60
+143d7 13 427 60
+143ea 46 430 60
+14430 3a 427 60
+1446a 3 432 60
+1446d 6 433 60
+14473 1 433 60
+FUNC 14474 85 0 dwarf2reader::CompilationUnit::SkipDIE(char const*, dwarf2reader::CompilationUnit::Abbrev const&)
+14474 c 122 60
+14480 13 123 60
+14493 27 126 60
+144ba 3a 123 60
+144f4 3 128 60
+144f7 2 129 60
+144f9 1 129 60
+FUNC 144fa be4 0 dwarf2reader::LineInfo::ProcessOneOpcode(dwarf2reader::ByteReader*, dwarf2reader::LineInfoHandler*, dwarf2reader::LineInfoHeader const&, char const*, dwarf2reader::LineStateMachine*, unsigned long*, unsigned long, bool*)
+144fa 18 593 60
+14512 a 594 60
+1451c 18 596 60
+14534 8 597 60
+1453c 5 598 60
+14541 19 602 60
+1455a f 603 60
+14569 50 605 60
+145b9 46 607 60
+145ff e0 610 60
+146df 6 612 60
+146e5 22 615 60
+14707 22 616 60
+14729 7 617 60
+14730 b 618 60
+1473b f 619 60
+1474a 5a 623 60
+147a4 7 625 60
+147ab b 626 60
+147b6 f 627 60
+147c5 28 631 60
+147ed e 632 60
+147fb 144 635 60
+1493f 6 637 60
+14945 9e 640 60
+149e3 5 642 60
+149e8 22 644 60
+14a0a e 645 60
+14a18 1c 646 60
+14a34 2b 652 60
+14a5f b 653 60
+14a6a 22 658 60
+14a8c e 659 60
+14a9a 8 660 60
+14aa2 5 662 60
+14aa7 22 664 60
+14ac9 e 665 60
+14ad7 9 666 60
+14ae0 5 668 60
+14ae5 12 670 60
+14af7 5 672 60
+14afc 7 674 60
+14b03 5 676 60
+14b08 16 678 60
+14b1e 9 679 60
+14b27 d9 682 60
+14c00 6 684 60
+14c06 1f 687 60
+14c25 5 689 60
+14c2a 40 693 60
+14c6a d6 696 60
+14d40 6 698 60
+14d46 1c 701 60
+14d62 5 703 60
+14d67 1f 706 60
+14d86 d 707 60
+14d93 13 708 60
+14da6 26 710 60
+14dcc 5 711 60
+14dd1 50 713 60
+14e21 7 715 60
+14e28 b 716 60
+14e33 f 717 60
+14e42 18 725 60
+14e5a f 726 60
+14e69 5 728 60
+14e6e 6 730 60
+14e74 28 732 60
+14e9c d 733 60
+14ea9 22 735 60
+14ecb e 736 60
+14ed9 22 739 60
+14efb e 740 60
+14f09 22 743 60
+14f2b e 744 60
+14f39 a 746 60
+14f43 fd 748 60
+15040 a 758 60
+1504a 9 759 60
+15053 1c 761 60
+1506f d 762 60
+1507c e 763 60
+1508a 2e 759 60
+150b8 b 769 60
+150c3 10 770 60
+150d3 b 771 60
+FUNC 150de 14b 0 dwarf2reader::LineInfo::ReadLines()
+150de e 773 60
+150ec 9 778 60
+150f5 17 782 60
+1510c 8 783 60
+15114 6 785 60
+1511a 9 787 60
+15123 5 788 60
+15128 19 789 60
+15141 5 790 60
+15146 4a 793 60
+15190 6 794 60
+15196 4a 796 60
+151e0 a 797 60
+151ea f 790 60
+151f9 15 788 60
+1520e 14 801 60
+15222 7 802 60
+15229 1 802 60
+FUNC 1522a 4fd 0 dwarf2reader::CompilationUnit::ReadAbbrevs()
+1522a 18 60 60
+15242 e 61 60
+15250 58 65 60
+152a8 38 66 60
+152e0 44 65 60
+15324 2a 66 60
+1534e 45 68 60
+15393 16 69 60
+153a9 1d 75 60
+153c6 6 76 60
+153cc 40 77 60
+1540c b 80 60
+15417 1f 82 60
+15436 e 84 60
+15444 6 77 60
+1544a 1f 68 60
+15469 a 84 60
+15473 1d 79 60
+15490 6 86 60
+15496 a 87 60
+154a0 3d 89 60
+154dd 1f 90 60
+154fc a 91 60
+15506 6 92 60
+1550c 3d 94 60
+15549 1d 95 60
+15566 5 96 60
+1556b 3d 98 60
+155a8 1f 101 60
+155c7 a 102 60
+155d1 3d 104 60
+1560e 1f 105 60
+1562d a 106 60
+15637 c 107 60
+15643 6 111 60
+15649 6 112 60
+1564f 32 113 60
+15681 47 115 60
+156c8 30 116 60
+156f8 24 79 60
+1571c b 118 60
+15727 1 118 60
+FUNC 15728 5dc 0 dwarf2reader::LineInfo::ReadHeader()
+15728 18 503 60
+15740 9 504 60
+15749 17 508 60
+15760 a 510 60
+1576a f 511 60
+15779 60 512 60
+157d9 44 516 60
+1581d 1e 518 60
+1583b 6 519 60
+15841 1e 521 60
+1585f 18 522 60
+15877 1d 524 60
+15894 5 525 60
+15899 20 527 60
+158b9 5 528 60
+158be c 530 60
+158ca 5 531 60
+158cf 1d 533 60
+158ec 5 534 60
+158f1 1d 536 60
+1590e 5 537 60
+15913 45 539 60
+15958 1f 540 60
+15977 19 541 60
+15990 15 542 60
+159a5 1f 539 60
+159c4 30 543 60
+159f4 5 544 60
+159f9 14 542 60
+15a0d e 548 60
+15a1b 7 549 60
+15a22 5 550 60
+15a27 6 551 60
+15a2d 8b 552 60
+15ab8 28 553 60
+15ae0 5 554 60
+15ae5 16 550 60
+15afb 25 552 60
+15b20 5 557 60
+15b25 e 560 60
+15b33 7 561 60
+15b3a 5 563 60
+15b3f 6 564 60
+15b45 28 565 60
+15b6d 22 567 60
+15b8f a 568 60
+15b99 22 570 60
+15bbb a 571 60
+15bc5 22 573 60
+15be7 a 574 60
+15bf1 ba 576 60
+15cab 5 577 60
+15cb0 16 563 60
+15cc6 25 576 60
+15ceb 5 580 60
+15cf0 9 582 60
+15cf9 b 583 60
+FUNC 15d04 3d 0 dwarf2reader::LineInfo::Start()
+15d04 c 495 60
+15d10 b 496 60
+15d1b b 497 60
+15d26 19 498 60
+15d3f 2 499 60
+15d41 1 499 60
+FUNC 15d42 304 0 dwarf2reader::CompilationUnit::ProcessDIEs()
+15d42 11 435 60
+15d53 9 436 60
+15d5c 9 441 60
+15d65 17 445 60
+15d7c 8 446 60
+15d84 6 448 60
+15d8a 6c 453 60
+15df6 8 455 60
+15dfe 16 453 60
+15e14 3 455 60
+15e17 2f 453 60
+15e46 29 458 60
+15e6f 22 460 60
+15e91 a 462 60
+15e9b a 465 60
+15ea5 1e 466 60
+15ec3 13 467 60
+15ed6 2b 468 60
+15f01 18 472 60
+15f19 9 473 60
+15f22 42 474 60
+15f64 1e 475 60
+15f82 2a 477 60
+15fac b 480 60
+15fb7 1e 481 60
+15fd5 26 483 60
+15ffb 1d 455 60
+16018 24 485 60
+1603c a 486 60
+FUNC 16046 35f 0 dwarf2reader::CompilationUnit::Start()
+16046 18 249 60
+1605e 58 251 60
+160b6 35 252 60
+160eb 32 251 60
+1611d 2a 252 60
+16147 20 255 60
+16167 37 256 60
+1619e b 259 60
+161a9 f 264 60
+161b8 17 265 60
+161cf c 266 60
+161db a 268 60
+161e5 95 271 60
+1627a 11 276 60
+1628b b 279 60
+16296 58 282 60
+162ee 2f 283 60
+1631d 32 282 60
+1634f 14 284 60
+16363 1a 285 60
+1637d b 289 60
+16388 12 291 60
+1639a b 292 60
+163a5 1 292 60
+FUNC 163a6 3a 0 std::fill(unsigned char*, unsigned char*, unsigned char const&)
+163a6 c 573 61
+163b2 9 576 61
+163bb 23 577 61
+163de 2 578 61
+FUNC 163e0 33 0 std::__deque_buf_size(unsigned long)
+163e0 c 83 62
+163ec 27 84 62
+16413 1 84 62
+FUNC 16414 18 0 dwarf2reader::ByteReader::OffsetSize() const
+16414 c 38 63
+16420 c 38 63
+FUNC 1642c 18 0 dwarf2reader::ByteReader::AddressSize() const
+1642c c 41 63
+16438 c 41 63
+FUNC 16444 17 0 dwarf2reader::ByteReader::ReadOneByte(char const*) const
+16444 c 10 64
+16450 9 11 64
+16459 2 12 64
+1645b 1 12 64
+FUNC 1645c 63 0 dwarf2reader::ByteReader::ReadTwoBytes(char const*) const
+1645c c 14 64
+16468 d 15 64
+16475 e 16 64
+16483 b 17 64
+1648e 17 18 64
+164a5 18 20 64
+164bd 2 22 64
+164bf 1 22 64
+FUNC 164c0 98 0 dwarf2reader::ByteReader::ReadUnsignedLEB128(char const*, unsigned long*) const
+164c0 e 59 64
+164ce e 60 64
+164dc 7 61 64
+164e3 7 62 64
+164ea e 66 64
+164f8 5 67 64
+164fd 38 69 64
+16535 6 71 64
+1653b 8 65 64
+16543 8 75 64
+1654b 6 77 64
+16551 7 78 64
+FUNC 16558 ee 0 dwarf2reader::ByteReader::ReadSignedLEB128(char const*, unsigned long*) const
+16558 e 84 64
+16566 e 85 64
+16574 7 86 64
+1657b 7 87 64
+16582 e 91 64
+16590 5 92 64
+16595 44 93 64
+165d9 6 94 64
+165df 8 90 64
+165e7 14 97 64
+165fb 36 98 64
+16631 8 99 64
+16639 6 100 64
+1663f 7 101 64
+FUNC 16646 a2 0 dwarf2reader::ByteReader::ReadOffset(char const*) const
+16646 13 103 64
+16659 3f 104 64
+16698 4a 105 64
+166e2 6 106 64
+FUNC 166e8 a2 0 dwarf2reader::ByteReader::ReadAddress(char const*) const
+166e8 13 108 64
+166fb 3f 109 64
+1673a 4a 110 64
+16784 6 111 64
+FUNC 1678a 61 0 dwarf2reader::LineStateMachine::Reset(bool)
+1678a 12 12 65
+1679c 9 13 65
+167a5 11 14 65
+167b6 11 15 65
+167c7 a 16 65
+167d1 a 17 65
+167db 7 18 65
+167e2 7 19 65
+167e9 2 20 65
+167eb 1 20 65
+FUNC 167ec 20 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator!=(std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > const&) const
+167ec c 253 66
+167f8 14 254 66
+FUNC 1680c 25 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator++(int)
+1680c c 226 66
+16818 8 228 66
+16820 c 229 66
+1682c 5 230 66
+16831 1 230 66
+FUNC 16832 16 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator->() const
+16832 c 215 66
+1683e a 216 66
+16848 c 190 67
+16854 a 190 67
+FUNC 1685e 13 0 std::auto_ptr<std::stack<unsigned long long, std::deque<unsigned long long, std::allocator<unsigned long long> > > >::operator->() const
+1685e c 283 67
+1686a 7 286 67
+16871 1 286 67
+16872 c 65 68
+1687e 2 65 68
+16880 c 97 69
+1688c d 97 69
+16899 1 97 69
+1689a c 99 69
+168a6 14 100 69
+168ba c 97 69
+168c6 d 97 69
+168d3 1 97 69
+168d4 c 84 70
+168e0 17 85 70
+168f7 1 85 70
+FUNC 168f8 2d 0 std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> std::make_pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>(dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm)
+168f8 c 144 70
+16904 21 145 70
+16925 1 145 70
+16926 c 202 66
+16932 a 203 66
+FUNC 1693c 25 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::begin() const
+1693c c 588 70
+16948 19 589 66
+16961 1 589 66
+FUNC 16962 23 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::end() const
+16962 c 605 70
+1696e 17 606 66
+16985 1 606 66
+16986 c 65 68
+16992 2 65 68
+16994 c 72 68
+169a0 2 72 68
+169a2 c 97 69
+169ae d 97 69
+169bb 1 97 69
+169bc c 105 69
+169c8 d 105 69
+169d5 1 105 69
+169d6 c 105 69
+169e2 d 105 69
+169ef 1 105 69
+169f0 c 67 68
+169fc 2 67 68
+169fe c 99 69
+16a0a 14 100 69
+16a1e c 99 69
+16a2a 14 100 69
+16a3e c 129 62
+16a4a 30 131 62
+16a7a c 65 68
+16a86 2 65 68
+16a88 c 72 68
+16a94 2 72 68
+16a96 c 97 69
+16aa2 d 97 69
+16aaf 1 97 69
+16ab0 c 105 69
+16abc d 105 69
+16ac9 1 105 69
+16aca c 105 69
+16ad6 d 105 69
+16ae3 1 105 69
+16ae4 c 67 68
+16af0 2 67 68
+16af2 c 99 69
+16afe 14 100 69
+16b12 c 99 69
+16b1e 14 100 69
+FUNC 16b32 2b 0 std::_Vector_base<unsigned char, std::allocator<unsigned char> >::get_allocator() const
+16b32 10 93 71
+16b42 1b 94 71
+16b5d 1 94 71
+16b6a 7 614 72
+16b71 1 614 72
+16b72 c 80 71
+16b7e d 80 71
+16b8b 1 80 71
+16b98 2 107 68
+FUNC 16b9a 2d 0 void std::_Destroy<unsigned char*, std::allocator<unsigned char> >(unsigned char*, unsigned char*, std::allocator<unsigned char>)
+16b9a c 171 73
+16ba6 2 173 73
+16ba8 12 174 73
+16bba b 173 73
+16bc5 2 174 73
+16bc7 1 174 73
+16bc8 c 84 71
+16bd4 2f 85 71
+16c03 2 86 71
+16c05 1 86 71
+16c06 c 96 71
+16c12 12 97 71
+16c24 2 98 71
+FUNC 16c26 1f 0 std::_List_base<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_init()
+16c26 c 338 73
+16c32 8 340 66
+16c3a b 341 66
+16c45 1 341 66
+16c46 c 105 69
+16c52 d 105 69
+16c5f 1 105 69
+16c60 c 125 66
+16c6c a 126 66
+FUNC 16c76 25 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::begin()
+16c76 c 579 73
+16c82 19 580 66
+16c9b 1 580 66
+FUNC 16c9c 23 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::end()
+16c9c c 597 73
+16ca8 17 597 66
+16cbf 1 597 66
+16cc0 c 603 72
+16ccc c 603 72
+FUNC 16cd8 2b 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::begin() const
+16cd8 c 342 73
+16ce4 1f 343 71
+16d03 1 343 71
+FUNC 16d04 2c 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::end() const
+16d04 c 360 73
+16d10 20 361 71
+16d3c 5 666 72
+16d41 1 666 72
+16d4f 31 759 72
+FUNC 16d80 3c 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::size() const
+16d80 c 402 73
+16d8c 30 403 71
+16dbc c 603 72
+16dc8 c 603 72
+FUNC 16dd4 23 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::begin()
+16dd4 c 333 73
+16de0 17 334 71
+16df7 1 334 71
+16e04 33 654 72
+16e37 1 654 72
+FUNC 16e38 26 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::end()
+16e38 c 351 73
+16e44 1a 352 71
+16e6a 7 614 72
+16e71 1 614 72
+FUNC 16e72 42 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::operator[](unsigned long)
+16e72 c 494 73
+16e7e 36 495 71
+FUNC 16eb4 13 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::max_size() const
+16eb4 c 407 73
+16ec0 7 408 71
+16ec7 1 408 71
+16ed4 5 666 72
+16ed9 1 666 72
+16ee6 d 623 72
+16ef3 5 624 72
+16ef8 c 382 62
+16f04 d 382 62
+16f11 1 382 62
+FUNC 16f12 2b 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::get_allocator() const
+16f12 10 360 73
+16f22 1b 361 62
+16f3d 1 361 62
+FUNC 16f3e 2d 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::get_allocator() const
+16f3e 10 764 73
+16f4e 1d 765 62
+16f6b 1 765 62
+FUNC 16f6c 13 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::operator*() const
+16f6c c 134 73
+16f78 7 135 62
+16f7f 1 135 62
+16f8c 2 107 68
+16f8e c 129 62
+16f9a 30 131 62
+FUNC 16fca 2c 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::end() const
+16fca 10 799 73
+16fda 1c 800 62
+FUNC 16ff6 2c 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::begin() const
+16ff6 10 781 73
+17006 1c 782 62
+FUNC 17022 2e 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::end()
+17022 10 790 73
+17032 1e 791 62
+FUNC 17050 3c 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::_M_range_check(unsigned long) const
+17050 13 515 73
+17063 15 517 71
+17078 14 518 71
+FUNC 1708c 32 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::at(unsigned long)
+1708c c 534 73
+17098 12 536 71
+170aa 14 537 71
+170ca 2e 104 68
+170f8 c 84 71
+17104 2f 85 71
+17133 2 86 71
+17135 1 86 71
+17136 c 96 71
+17142 12 97 71
+17154 2 98 71
+17156 c 603 72
+17162 c 603 72
+FUNC 1716e 23 0 std::vector<unsigned char, std::allocator<unsigned char> >::begin()
+1716e c 333 73
+1717a 17 334 71
+17191 1 334 71
+1719e 27 654 72
+171c5 1 654 72
+FUNC 171c6 42 0 std::vector<unsigned char, std::allocator<unsigned char> >::operator[](unsigned long)
+171c6 c 494 73
+171d2 36 495 71
+FUNC 17208 26 0 std::vector<unsigned char, std::allocator<unsigned char> >::end()
+17208 c 351 73
+17214 1a 352 71
+1723a d 94 68
+17247 1 94 68
+FUNC 17248 2f 0 std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_deallocate(unsigned char*, unsigned long)
+17248 c 120 73
+17254 6 122 71
+1725a 1d 123 71
+17277 1 123 71
+17278 c 108 71
+17284 3a 109 71
+172be c 188 71
+172ca 12 189 71
+172dc 2 190 71
+172de c 272 71
+172ea 4b 273 71
+17335 1 273 71
+17336 13 62 74
+17349 10 62 74
+17359 a 63 74
+17363 25 64 74
+17388 1a 66 74
+173a2 13 62 74
+173b5 10 62 74
+173c5 a 63 74
+173cf 25 64 74
+173f4 1a 66 74
+1740e c 188 71
+1741a 12 189 71
+1742c 2 190 71
+1743b 31 759 72
+1746c c 65 68
+17478 2 65 68
+1747a c 103 69
+17486 d 103 69
+17493 1 103 69
+FUNC 17494 2d 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::get_allocator() const
+17494 10 570 74
+174a4 1d 571 66
+174c1 1 571 66
+174ce 2e 104 68
+FUNC 174fc 20 0 std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator!=(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > const&) const
+174fc c 172 74
+17508 14 173 66
+FUNC 1751c 1d 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator++()
+1751c c 219 74
+17528 c 221 66
+17534 5 222 66
+17539 1 222 66
+FUNC 1753a 1d 0 std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator++()
+1753a c 138 74
+17546 c 140 66
+17552 5 141 66
+17557 1 141 66
+FUNC 17558 16 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator*() const
+17558 c 211 74
+17564 a 212 66
+FUNC 1756e 16 0 std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator*() const
+1756e c 130 74
+1757a a 131 66
+FUNC 17584 20 0 std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >::operator==(std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > const&) const
+17584 c 249 74
+17590 14 250 66
+FUNC 175a4 35 0 bool __gnu_cxx::operator!=<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > > const&, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > > const&)
+175a4 d 699 74
+175b1 28 700 72
+175d9 1 700 72
+FUNC 175da 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+175da c 171 74
+175e6 2 173 73
+175e8 1a 174 73
+17602 21 173 73
+17623 2 174 73
+17625 1 174 73
+17626 c 127 62
+17632 29 127 62
+1765b 1 127 62
+1765c c 388 62
+17668 41 389 62
+176a9 2 390 62
+176ab 1 390 62
+176b8 d 94 68
+176c5 1 94 68
+FUNC 176c6 20 0 bool std::operator==<unsigned long long, unsigned long long&, unsigned long long*>(std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&)
+176c6 c 243 74
+176d2 14 244 62
+FUNC 176e6 26 0 bool std::operator!=<unsigned long long, unsigned long long&, unsigned long long*>(std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&)
+176e6 c 256 74
+176f2 1a 257 62
+FUNC 1770c 1a 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::_S_buffer_size()
+1770c c 106 74
+17718 e 107 62
+FUNC 17726 3e 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::_M_set_node(unsigned long long**)
+17726 d 229 74
+17733 9 231 62
+1773c b 232 62
+17747 1d 233 62
+FUNC 17764 50 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::operator++()
+17764 c 142 74
+17770 d 144 62
+1777d f 145 62
+1778c 18 147 62
+177a4 b 148 62
+177af 5 150 62
+FUNC 177b4 4b 0 void std::_Destroy<std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, std::allocator<unsigned long long> >(std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, std::allocator<unsigned long long>)
+177b4 c 171 74
+177c0 2 173 73
+177c2 1a 174 73
+177dc 21 173 73
+177fd 2 174 73
+177ff 1 174 73
+FUNC 17800 50 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::operator--()
+17800 c 162 74
+1780c f 164 62
+1781b 18 166 62
+17833 b 167 62
+1783e d 169 62
+1784b 5 170 62
+FUNC 17850 39 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::back()
+17850 c 988 74
+1785c 15 990 62
+17871 b 991 62
+1787c d 992 62
+17889 1 992 62
+FUNC 1788a 19 0 std::stack<unsigned long long, std::deque<unsigned long long, std::allocator<unsigned long long> > >::top()
+1788a c 163 75
+17896 d 166 75
+178a3 1 166 75
+FUNC 178a4 66 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>::difference_type std::operator-<unsigned long long, unsigned long long&, unsigned long long*, unsigned long long&, unsigned long long*>(std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> const&)
+178a4 d 328 75
+178b1 59 333 62
+FUNC 1790a 26 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::size() const
+1790a c 840 75
+17916 1a 841 62
+1793c 36 662 72
+1797e 23 650 72
+179a1 1 650 72
+179a2 c 67 68
+179ae 2 67 68
+179b0 c 99 69
+179bc 14 100 69
+179d0 c 303 66
+179dc 12 304 66
+179ee 2 305 66
+179f0 c 326 66
+179fc 2f 327 66
+17a2b d 328 66
+17a38 c 457 66
+17a44 14 458 66
+17a58 c 211 74
+17a64 2d 211 74
+17a91 1 211 74
+17a9e 7 98 68
+17aa5 1 98 68
+17ab2 1d 85 68
+17acf 5 86 68
+17ad4 17 88 68
+17aeb 1 88 68
+FUNC 17aec 2a 0 std::_Vector_base<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::_M_allocate(unsigned long)
+17aec c 116 75
+17af8 1e 117 71
+17b22 d 94 68
+17b2f 1 94 68
+FUNC 17b30 34 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_deallocate_node(unsigned long long*)
+17b30 c 402 75
+17b3c 28 403 62
+FUNC 17b64 38 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_destroy_nodes(unsigned long long**, unsigned long long**)
+17b64 c 504 75
+17b70 8 506 62
+17b78 14 507 62
+17b8c e 506 62
+17b9a 2 507 62
+FUNC 17b9c 62 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::_M_pop_back_aux()
+17b9c c 391 76
+17ba8 15 393 76
+17bbd 1b 394 76
+17bd8 f 395 76
+17be7 17 396 76
+FUNC 17bfe 4f 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::pop_back()
+17bfe c 1081 76
+17c0a 10 1083 62
+17c1a f 1086 62
+17c29 17 1087 62
+17c40 d 1090 62
+17c4d 1 1090 62
+FUNC 17c4e 19 0 std::stack<unsigned long long, std::deque<unsigned long long, std::allocator<unsigned long long> > >::pop()
+17c4e c 205 76
+17c5a d 208 75
+17c67 1 208 75
+17c68 c 72 68
+17c74 2 72 68
+17c76 c 105 69
+17c82 d 105 69
+17c8f 1 105 69
+17c90 c 603 72
+17c9c c 603 72
+FUNC 17ca8 2b 0 std::vector<unsigned char, std::allocator<unsigned char> >::begin() const
+17ca8 c 342 76
+17cb4 1f 343 71
+17cd3 1 343 71
+FUNC 17cd4 2c 0 std::vector<unsigned char, std::allocator<unsigned char> >::end() const
+17cd4 c 360 76
+17ce0 20 361 71
+17d0c 5 666 72
+17d11 1 666 72
+17d1f 28 759 72
+17d47 1 759 72
+FUNC 17d48 3c 0 std::vector<unsigned char, std::allocator<unsigned char> >::size() const
+17d48 c 402 76
+17d54 30 403 71
+17d90 d 623 72
+17d9d 5 624 72
+17dae 5 666 72
+17db3 1 666 72
+FUNC 17db4 35 0 bool __gnu_cxx::operator!=<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > > const&, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > > const&)
+17db4 d 699 76
+17dc1 28 700 72
+17de9 1 700 72
+FUNC 17dea 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, std::allocator<unsigned char> >(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, std::allocator<unsigned char>)
+17dea c 171 76
+17df6 2 173 73
+17df8 1a 174 73
+17e12 21 173 73
+17e33 2 174 73
+17e35 1 174 73
+17e43 28 759 72
+17e6b 1 759 72
+17e78 2a 662 72
+FUNC 17ea2 13 0 std::vector<unsigned char, std::allocator<unsigned char> >::max_size() const
+17ea2 c 407 76
+17eae 7 408 71
+17eb5 1 408 71
+17ec2 16 650 72
+17ee4 7 98 68
+17eeb 1 98 68
+17ef8 1d 85 68
+17f15 5 86 68
+17f1a 10 88 68
+FUNC 17f2a 29 0 std::_List_base<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_get_node()
+17f2a c 311 76
+17f36 1d 312 66
+17f53 1 312 66
+FUNC 17f54 5f 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_create_node(std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> const&)
+17f54 d 435 76
+17f61 e 437 66
+17f6f 3c 440 66
+17fab 8 447 66
+17fb3 1 447 66
+FUNC 17fb4 35 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_insert(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> const&)
+17fb4 c 1149 76
+17fc0 15 1151 66
+17fd5 14 1152 66
+17fe9 1 1152 66
+FUNC 17fea 52 0 void std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_insert_dispatch<std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, __false_type)
+17fea c 1126 66
+17ff6 2 1128 66
+17ff8 21 1129 66
+18019 21 1128 66
+1803a 2 1129 66
+FUNC 1803c 36 0 void std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::insert<std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::_List_const_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >)
+1803c c 838 66
+18048 2a 842 66
+18072 e 491 66
+18080 32 492 66
+180b2 64 493 66
+18116 c 211 74
+18122 3d 211 74
+1815f 1 211 74
+1816d 5c 104 68
+181c9 1 104 68
+FUNC 181ca 31 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::push_back(std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> const&)
+181ca c 772 76
+181d6 25 773 66
+181fb 1 773 66
+FUNC 181fc 69 0 void std::_Construct<dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev const&)
+181fc d 77 76
+18209 5c 81 73
+18265 1 81 73
+18272 7 98 68
+18279 1 98 68
+18286 1d 85 68
+182a3 5 86 68
+182a8 10 88 68
+182b8 c 65 68
+182c4 2 65 68
+182c6 c 103 69
+182d2 d 103 69
+182df 1 103 69
+FUNC 182e0 4d 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_get_map_allocator() const
+182e0 11 394 76
+182f1 3c 395 62
+1832d 1 395 62
+FUNC 1832e 75 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_allocate_map(unsigned long)
+1832e d 406 76
+1833b 68 407 62
+183a3 1 407 62
+FUNC 183a4 47 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_deallocate_map(unsigned long long**, unsigned long)
+183a4 c 410 76
+183b0 3b 411 62
+183eb 1 411 62
+183ec c 424 62
+183f8 9 426 62
+18401 22 428 62
+18423 2b 430 62
+1844e c 714 62
+1845a 70 715 62
+184ca c 111 75
+184d6 d 111 75
+184e3 1 111 75
+184e4 c 259 67
+184f0 26 259 67
+18522 7 98 68
+18529 1 98 68
+18536 1d 85 68
+18553 5 86 68
+18558 10 88 68
+FUNC 18568 33 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_allocate_node()
+18568 c 398 76
+18574 27 399 62
+1859b 1 399 62
+FUNC 1859c 82 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_create_nodes(unsigned long long**, unsigned long long**)
+1859c d 486 76
+185a9 8 491 62
+185b1 12 492 62
+185c3 13 491 62
+185d6 b 494 62
+185e1 19 496 62
+185fa b 497 62
+18605 13 494 62
+18618 6 497 62
+FUNC 1861e 17b 0 std::_Deque_base<unsigned long long, std::allocator<unsigned long long> >::_M_initialize_map(unsigned long)
+1861e d 447 76
+1862b 1e 450 62
+18649 2a 452 62
+18673 1c 454 62
+1868f 19 462 62
+186a8 c 463 62
+186b4 1e 466 62
+186d2 b 467 62
+186dd 1e 469 62
+186fb 9 470 62
+18704 a 471 62
+1870e b 472 62
+18719 13 467 62
+1872c 15 475 62
+18741 18 476 62
+18759 c 477 62
+18765 34 478 62
+18799 1 478 62
+1879a d 366 62
+187a7 12 367 62
+187b9 39 368 62
+187f2 c 645 62
+187fe 1c 646 62
+FUNC 1881a 4d 0 void std::__fill<true>::fill<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char const&)
+1881a c 539 61
+18826 9 541 61
+1882f 2 542 61
+18831 13 543 61
+18844 21 542 61
+18865 2 543 61
+18867 1 543 61
+FUNC 18868 2b 0 void std::fill<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char const&)
+18868 c 560 76
+18874 4 567 61
+18878 1b 568 61
+18893 1 568 61
+FUNC 18894 6a 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::_M_erase(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >)
+18894 d 1157 76
+188a1 b 1159 66
+188ac 6 1160 66
+188b2 35 1161 66
+188e7 17 1162 66
+FUNC 188fe 37 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::erase(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >)
+188fe c 95 77
+1890a 14 97 77
+1891e 12 98 77
+18930 5 99 77
+18935 1 99 77
+FUNC 18936 3e 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::erase(std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >, std::_List_iterator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> >)
+18936 c 883 77
+18942 2 885 66
+18944 15 886 66
+18959 16 885 66
+1896f 5 887 66
+FUNC 18974 129 0 std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > >::operator=(std::list<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm>, std::allocator<std::pair<dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm> > > const&)
+18974 e 120 77
+18982 c 122 77
+1898e e 124 77
+1899c e 125 77
+189aa e 126 77
+189b8 e 127 77
+189c6 2 128 77
+189c8 20 130 77
+189e8 5a 128 77
+18a42 16 131 77
+18a58 1b 132 77
+18a73 20 134 77
+18a93 a 136 77
+18a9d 1 136 77
+FUNC 18a9e 4c 0 dwarf2reader::CompilationUnit::Abbrev::operator=(dwarf2reader::CompilationUnit::Abbrev const&)
+18a9e c 211 77
+18aaa 40 211 74
+FUNC 18aea 52 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy<false, std::random_access_iterator_tag>::copy<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+18aea c 280 61
+18af6 1a 283 61
+18b10 12 285 61
+18b22 4 286 61
+18b26 6 287 61
+18b2c b 283 61
+18b37 5 289 61
+FUNC 18b3c 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_aux<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+18b3c c 307 77
+18b48 4 315 61
+18b4c 1b 317 61
+18b67 1 317 61
+18b76 56 354 61
+18bd8 4 384 61
+18bdc 4 385 61
+18be0 1b 387 61
+18bfb 1 387 61
+FUNC 18bfc ac 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::erase(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >)
+18bfc d 122 78
+18c09 26 124 78
+18c2f 43 125 78
+18c72 2e 126 78
+18ca0 8 127 78
+FUNC 18ca8 54 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward<false, std::random_access_iterator_tag>::copy_b<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+18ca8 c 408 61
+18cb4 1a 411 61
+18cce 1e 412 61
+18cec b 411 61
+18cf7 5 413 61
+FUNC 18cfc 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward_aux<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+18cfc c 432 78
+18d08 4 440 61
+18d0c 1b 443 61
+18d27 1 443 61
+18d36 56 482 61
+18d98 4 514 61
+18d9c 4 515 61
+18da0 1b 517 61
+18dbb 1 517 61
+FUNC 18dbc 4d 0 void std::__fill<false>::fill<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev const&)
+18dbc c 526 61
+18dc8 2 528 61
+18dca 1c 529 61
+18de6 21 528 61
+18e07 2 529 61
+18e09 1 529 61
+FUNC 18e0a 2b 0 void std::fill<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev const&)
+18e0a c 560 78
+18e16 4 567 61
+18e1a 1b 568 61
+18e35 1 568 61
+FUNC 18e36 3f 0 unsigned char* std::__copy<true, std::random_access_iterator_tag>::copy<unsigned char>(unsigned char const*, unsigned char const*, unsigned char*)
+18e36 c 298 61
+18e42 22 300 61
+18e64 11 301 61
+18e75 1 301 61
+FUNC 18e76 2b 0 unsigned char* std::__copy_aux<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*)
+18e76 c 307 78
+18e82 4 315 61
+18e86 1b 317 61
+18ea1 1 317 61
+18eb0 56 354 61
+18f12 4 384 61
+18f16 4 385 61
+18f1a 1b 387 61
+18f35 1 387 61
+FUNC 18f36 a0 0 std::vector<unsigned char, std::allocator<unsigned char> >::erase(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >)
+18f36 d 122 78
+18f43 26 124 78
+18f69 43 125 78
+18fac 22 126 78
+18fce 8 127 78
+18fe2 7 98 68
+18fe9 1 98 68
+18ff6 1d 85 68
+19013 5 86 68
+19018 d 88 68
+19025 1 88 68
+FUNC 19026 2a 0 std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_allocate(unsigned long)
+19026 c 116 78
+19032 1e 117 71
+1905c 1b 74 79
+19077 1 74 79
+19084 23 113 79
+190a7 1 113 79
+190b4 1b 254 79
+190cf 1 254 79
+FUNC 190d0 19 0 void std::_Destroy<dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*)
+190d0 c 106 79
+190dc d 107 73
+190e9 1 107 73
+FUNC 190ea 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > > >(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __false_type)
+190ea c 119 79
+190f6 2 121 73
+190f8 13 122 73
+1910b 21 121 73
+1912c 2 122 73
+FUNC 1912e 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > > >(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >)
+1912e c 148 79
+1913a 1c 155 73
+FUNC 19156 8d 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type)
+19156 d 188 79
+19163 6 190 79
+19169 2 193 79
+1916b 1c 194 79
+19187 1b 193 79
+191a2 b 196 79
+191ad 12 198 79
+191bf b 199 79
+191ca 13 196 79
+191dd 6 199 79
+191e3 1 199 79
+FUNC 191e4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&)
+191e4 c 214 79
+191f0 23 218 79
+19213 1 218 79
+FUNC 19214 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+19214 c 308 79
+19220 1b 310 79
+1923b 1 310 79
+19249 6 82 79
+1924f 2 85 79
+19251 24 86 79
+19275 2c 85 79
+192a1 b 87 79
+192ac b 89 79
+192b7 12 91 79
+192c9 b 92 79
+192d4 13 89 79
+192e7 9 92 79
+192fc 23 113 79
+1931f 1 113 79
+1932c 1b 254 79
+19347 1 254 79
+FUNC 19348 409 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::_M_insert_aux(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev const&)
+19348 14 249 79
+1935c 14 251 78
+19370 22 253 78
+19392 f 255 78
+193a1 12 256 78
+193b3 55 257 78
+19408 4b 260 78
+19453 e 264 78
+19461 15 265 78
+19476 e 266 78
+19484 1d 271 78
+194a1 8 272 78
+194a9 e 273 78
+194b7 27 275 78
+194de 6 276 78
+194e4 55 279 78
+19539 25 284 78
+1955e b 285 78
+19569 4f 286 78
+195b8 3 284 78
+195bb 13 279 78
+195ce e 286 78
+195dc 4d 298 78
+19629 36 299 78
+1965f 12 302 78
+19671 13 303 78
+19684 2e 304 78
+196b2 13 286 78
+196c5 b 292 78
+196d0 39 294 78
+19709 23 295 78
+1972c b 296 78
+19737 13 292 78
+1974a 7 304 78
+19751 1 304 78
+FUNC 19752 70 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::push_back(dwarf2reader::CompilationUnit::Abbrev const&)
+19752 c 602 79
+1975e 10 604 71
+1976e 1e 606 71
+1978c 11 607 71
+1979d 25 610 71
+FUNC 197c2 50 0 unsigned char* std::__copy_backward<true, std::random_access_iterator_tag>::copy_b<unsigned char>(unsigned char const*, unsigned char const*, unsigned char*)
+197c2 d 422 61
+197cf f 424 61
+197de 24 425 61
+19802 10 426 61
+FUNC 19812 2b 0 unsigned char* std::__copy_backward_aux<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*)
+19812 c 432 79
+1981e 4 440 61
+19822 1b 443 61
+1983d 1 443 61
+1984c 56 482 61
+198ae 4 514 61
+198b2 4 515 61
+198b6 1b 517 61
+198d1 1 517 61
+FUNC 198d2 32 0 unsigned char* std::fill_n<unsigned long>(unsigned char*, unsigned long, unsigned char const&)
+198d2 c 647 79
+198de 1e 649 61
+198fc 8 650 61
+FUNC 19904 27 0 void std::__uninitialized_fill_n_aux<unsigned char*, unsigned long, unsigned char>(unsigned char*, unsigned long, unsigned char const&, __true_type)
+19904 c 182 79
+19910 1b 183 79
+1992b 1 183 79
+FUNC 1992c 2f 0 void std::uninitialized_fill_n<unsigned char*, unsigned long, unsigned char>(unsigned char*, unsigned long, unsigned char const&)
+1992c c 214 79
+19938 23 218 79
+1995b 1 218 79
+FUNC 1995c 27 0 void std::__uninitialized_fill_n_a<unsigned char*, unsigned long, unsigned char, unsigned char>(unsigned char*, unsigned long, unsigned char const&, std::allocator<unsigned char>)
+1995c c 308 79
+19968 1b 310 79
+19983 1 310 79
+FUNC 19984 27 0 void std::__destroy_aux<dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type)
+19984 c 119 79
+19990 2 121 73
+19992 b 122 73
+1999d c 121 73
+199a9 2 122 73
+199ab 1 122 73
+FUNC 199ac 28 0 void std::_Destroy<dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+199ac c 148 79
+199b8 1c 155 73
+FUNC 199d4 88 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type)
+199d4 d 80 79
+199e1 6 82 79
+199e7 2 85 79
+199e9 12 86 79
+199fb 12 85 79
+19a0d b 87 79
+19a18 b 89 79
+19a23 12 91 79
+19a35 b 92 79
+19a40 13 89 79
+19a53 9 92 79
+FUNC 19a5c 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*)
+19a5c c 108 79
+19a68 23 113 79
+19a8b 1 113 79
+FUNC 19a8c 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a<dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+19a8c c 252 79
+19a98 1b 254 79
+19ab3 1 254 79
+FUNC 19ab4 7e 0 void std::__uninitialized_fill_n_aux<dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type)
+19ab4 d 188 79
+19ac1 6 190 79
+19ac7 2 193 79
+19ac9 12 194 79
+19adb 16 193 79
+19af1 b 196 79
+19afc 12 198 79
+19b0e b 199 79
+19b19 13 196 79
+19b2c 6 199 79
+FUNC 19b32 2f 0 void std::uninitialized_fill_n<dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&)
+19b32 c 214 79
+19b3e 23 218 79
+19b61 1 218 79
+FUNC 19b62 27 0 void std::__uninitialized_fill_n_a<dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+19b62 c 308 79
+19b6e 1b 310 79
+19b89 1 310 79
+FUNC 19b8a a5 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*, __false_type)
+19b8a d 80 79
+19b97 6 82 79
+19b9d 2 85 79
+19b9f 1a 86 79
+19bb9 27 85 79
+19be0 b 87 79
+19beb b 89 79
+19bf6 12 91 79
+19c08 b 92 79
+19c13 13 89 79
+19c26 9 92 79
+19c2f 1 92 79
+FUNC 19c30 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*)
+19c30 c 108 79
+19c3c 23 113 79
+19c5f 1 113 79
+FUNC 19c60 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, __gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, dwarf2reader::CompilationUnit::Abbrev*, std::allocator<dwarf2reader::CompilationUnit::Abbrev>)
+19c60 c 252 79
+19c6c 1b 254 79
+19c87 1 254 79
+FUNC 19c88 5f8 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::_M_fill_insert(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&)
+19c88 15 311 79
+19c9d b 313 78
+19ca8 2a 315 78
+19cd2 12 318 78
+19ce4 23 319 78
+19d07 15 320 78
+19d1c c 321 78
+19d28 5a 323 78
+19d82 1c 327 78
+19d9e 35 328 78
+19dd3 16 323 78
+19de9 30 330 78
+19e19 10 343 78
+19e29 48 334 78
+19e71 21 338 78
+19e92 3d 339 78
+19ecf 13 334 78
+19ee2 b 339 78
+19eed 1c 342 78
+19f09 1e 343 78
+19f27 13 339 78
+19f3a 24 343 78
+19f5e e 348 78
+19f6c 1e 349 78
+19f8a e 350 78
+19f98 1d 353 78
+19fb5 8 354 78
+19fbd e 355 78
+19fcb 27 357 78
+19ff2 6 358 78
+19ff8 4d 361 78
+1a045 40 365 78
+1a085 18 367 78
+1a09d 44 368 78
+1a0e1 3 365 78
+1a0e4 19 361 78
+1a0fd 13 365 78
+1a110 e 368 78
+1a11e 3e 379 78
+1a15c 36 381 78
+1a192 12 384 78
+1a1a4 13 385 78
+1a1b7 2e 386 78
+1a1e5 e 368 78
+1a1f3 b 372 78
+1a1fe 39 374 78
+1a237 23 376 78
+1a25a b 377 78
+1a265 13 372 78
+1a278 8 386 78
+FUNC 1a280 2e 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::insert(__gnu_cxx::__normal_iterator<dwarf2reader::CompilationUnit::Abbrev*, std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&)
+1a280 c 657 79
+1a28c 22 658 71
+FUNC 1a2ae ab 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::resize(unsigned long, dwarf2reader::CompilationUnit::Abbrev const&)
+1a2ae d 422 79
+1a2bb 15 424 71
+1a2d0 48 425 71
+1a318 41 427 71
+1a359 1 427 71
+FUNC 1a35a 63 0 std::vector<dwarf2reader::CompilationUnit::Abbrev, std::allocator<dwarf2reader::CompilationUnit::Abbrev> >::resize(unsigned long)
+1a35a d 441 79
+1a367 56 442 71
+1a3bd 1 442 71
+FUNC 1a3be 13 0 std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>::operator*() const
+1a3be c 134 79
+1a3ca 7 135 62
+1a3d1 1 135 62
+FUNC 1a3d2 3f 0 unsigned long long** std::__copy<true, std::random_access_iterator_tag>::copy<unsigned long long*>(unsigned long long* const*, unsigned long long* const*, unsigned long long**)
+1a3d2 c 298 61
+1a3de 22 300 61
+1a400 11 301 61
+1a411 1 301 61
+FUNC 1a412 2b 0 unsigned long long** std::__copy_aux<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a412 c 307 79
+1a41e 4 315 61
+1a422 1b 317 61
+1a43d 1 317 61
+FUNC 1a43e 27 0 unsigned long long** std::__copy_normal<false, false>::copy_n<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a43e c 325 61
+1a44a 1b 326 61
+1a465 1 326 61
+FUNC 1a466 2f 0 unsigned long long** std::copy<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a466 c 376 79
+1a472 4 384 61
+1a476 4 385 61
+1a47a 1b 387 61
+1a495 1 387 61
+FUNC 1a496 60 0 unsigned long long** std::__copy_backward<true, std::random_access_iterator_tag>::copy_b<unsigned long long*>(unsigned long long* const*, unsigned long long* const*, unsigned long long**)
+1a496 d 422 61
+1a4a3 12 424 61
+1a4b5 2e 425 61
+1a4e3 13 426 61
+FUNC 1a4f6 2b 0 unsigned long long** std::__copy_backward_aux<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a4f6 c 432 79
+1a502 4 440 61
+1a506 1b 443 61
+1a521 1 443 61
+FUNC 1a522 27 0 unsigned long long** std::__copy_backward_normal<false, false>::copy_b_n<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a522 c 451 61
+1a52e 1b 452 61
+1a549 1 452 61
+FUNC 1a54a 2f 0 unsigned long long** std::copy_backward<unsigned long long**, unsigned long long**>(unsigned long long**, unsigned long long**, unsigned long long**)
+1a54a c 504 79
+1a556 4 514 61
+1a55a 4 515 61
+1a55e 1b 517 61
+1a579 1 517 61
+FUNC 1a57a 1df 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::_M_reallocate_map(unsigned long, bool)
+1a57a 13 723 79
+1a58d 1b 726 76
+1a5a8 9 727 76
+1a5b1 13 730 76
+1a5c4 39 732 76
+1a5fd b 735 76
+1a608 27 736 76
+1a62f 2f 740 76
+1a65e 26 748 76
+1a684 15 750 76
+1a699 36 751 76
+1a6cf 22 753 76
+1a6f1 1e 756 76
+1a70f 8 758 76
+1a717 9 759 76
+1a720 15 762 76
+1a735 24 763 76
+1a759 1 763 76
+FUNC 1a75a 59 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::_M_reserve_map_at_back(unsigned long)
+1a75a e 1443 79
+1a768 2a 1445 62
+1a792 21 1447 62
+1a7b3 1 1447 62
+FUNC 1a7b4 8c 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::_M_push_back_aux(unsigned long long const&)
+1a7b4 c 345 79
+1a7c0 e 347 76
+1a7ce 13 348 76
+1a7e1 18 349 76
+1a7f9 1e 352 76
+1a817 1b 353 76
+1a832 c 355 76
+1a83e 2 360 76
+FUNC 1a840 62 0 std::deque<unsigned long long, std::allocator<unsigned long long> >::push_back(unsigned long long const&)
+1a840 c 1039 79
+1a84c 13 1041 62
+1a85f 1e 1044 62
+1a87d 11 1045 62
+1a88e 14 1048 62
+FUNC 1a8a2 20 0 std::stack<unsigned long long, std::deque<unsigned long long, std::allocator<unsigned long long> > >::push(unsigned long long const&)
+1a8a2 c 190 79
+1a8ae 14 191 75
+FUNC 1a8c2 27 0 unsigned char* std::__copy_normal<false, false>::copy_n<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*)
+1a8c2 c 325 61
+1a8ce 1b 326 61
+1a8e9 1 326 61
+FUNC 1a8ea 2f 0 unsigned char* std::copy<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*)
+1a8ea c 376 79
+1a8f6 4 384 61
+1a8fa 4 385 61
+1a8fe 1b 387 61
+1a919 1 387 61
+FUNC 1a91a 27 0 unsigned char* std::__uninitialized_copy_aux<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*, __true_type)
+1a91a c 73 79
+1a926 1b 74 79
+1a941 1 74 79
+FUNC 1a942 2f 0 unsigned char* std::uninitialized_copy<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*)
+1a942 c 108 79
+1a94e 23 113 79
+1a971 1 113 79
+FUNC 1a972 27 0 unsigned char* std::__uninitialized_copy_a<unsigned char*, unsigned char*, unsigned char>(unsigned char*, unsigned char*, unsigned char*, std::allocator<unsigned char>)
+1a972 c 252 79
+1a97e 1b 254 79
+1a999 1 254 79
+FUNC 1a99a 40 0 unsigned char* std::__copy_normal<true, false>::copy_n<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*)
+1a99a d 334 61
+1a9a7 33 335 61
+FUNC 1a9da 2f 0 unsigned char* std::copy<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*)
+1a9da c 376 79
+1a9e6 4 384 61
+1a9ea 4 385 61
+1a9ee 1b 387 61
+1aa09 1 387 61
+FUNC 1aa0a 27 0 unsigned char* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*, __true_type)
+1aa0a c 73 79
+1aa16 1b 74 79
+1aa31 1 74 79
+FUNC 1aa32 2f 0 unsigned char* std::uninitialized_copy<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*)
+1aa32 c 108 79
+1aa3e 23 113 79
+1aa61 1 113 79
+FUNC 1aa62 27 0 unsigned char* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned char*, std::allocator<unsigned char>)
+1aa62 c 252 79
+1aa6e 1b 254 79
+1aa89 1 254 79
+1aa96 9 616 61
+1aa9f 2 617 61
+1aaa1 13 618 61
+1aab4 16 617 61
+1aaca 5 619 61
+1aacf 1 619 61
+1aadc 4 641 61
+1aae0 1b 642 61
+1aafb 1 642 61
+FUNC 1aafc 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char const&, __true_type)
+1aafc c 182 79
+1ab08 1b 183 79
+1ab23 1 183 79
+FUNC 1ab24 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char const&)
+1ab24 c 214 79
+1ab30 23 218 79
+1ab53 1 218 79
+FUNC 1ab54 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char, unsigned char>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char const&, std::allocator<unsigned char>)
+1ab54 c 308 79
+1ab60 1b 310 79
+1ab7b 1 310 79
+FUNC 1ab7c 45a 0 std::vector<unsigned char, std::allocator<unsigned char> >::_M_fill_insert(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char const&)
+1ab7c 14 311 79
+1ab90 b 313 78
+1ab9b 21 315 78
+1abbc 9 318 78
+1abc5 23 319 78
+1abe8 15 320 78
+1abfd c 321 78
+1ac09 4e 323 78
+1ac57 11 327 78
+1ac68 30 328 78
+1ac98 35 330 78
+1accd 48 334 78
+1ad15 14 338 78
+1ad29 43 339 78
+1ad6c 11 342 78
+1ad7d 1e 343 78
+1ad9b e 348 78
+1ada9 1e 349 78
+1adc7 e 350 78
+1add5 1d 353 78
+1adf2 8 354 78
+1adfa e 355 78
+1ae08 27 357 78
+1ae2f 6 358 78
+1ae35 4d 361 78
+1ae82 40 365 78
+1aec2 18 367 78
+1aeda 4d 368 78
+1af27 3e 379 78
+1af65 2d 381 78
+1af92 12 384 78
+1afa4 13 385 78
+1afb7 1f 386 78
+FUNC 1afd6 2e 0 std::vector<unsigned char, std::allocator<unsigned char> >::insert(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >, unsigned long, unsigned char const&)
+1afd6 c 657 79
+1afe2 22 658 71
+FUNC 1b004 ab 0 std::vector<unsigned char, std::allocator<unsigned char> >::resize(unsigned long, unsigned char const&)
+1b004 d 422 79
+1b011 15 424 71
+1b026 48 425 71
+1b06e 41 427 71
+1b0af 1 427 71
+FUNC 1b0b0 2b 0 std::vector<unsigned char, std::allocator<unsigned char> >::resize(unsigned long)
+1b0b0 c 441 79
+1b0bc 1f 442 71
+1b0db 1 442 71
+FUNC 1b0dc 1a 0 std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>::_S_buffer_size()
+1b0dc c 106 79
+1b0e8 e 107 62
+FUNC 1b0f6 66 0 std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>::difference_type std::operator-<unsigned long long, unsigned long long const&, unsigned long long const*, unsigned long long const&, unsigned long long const*>(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*> const&, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*> const&)
+1b0f6 d 328 79
+1b103 59 333 62
+FUNC 1b15c 3e 0 std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>::_M_set_node(unsigned long long**)
+1b15c d 229 79
+1b169 9 231 62
+1b172 b 232 62
+1b17d 1d 233 62
+FUNC 1b19a 50 0 std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>::operator++()
+1b19a c 142 79
+1b1a6 d 144 62
+1b1b3 f 145 62
+1b1c2 18 147 62
+1b1da b 148 62
+1b1e5 5 150 62
+FUNC 1b1ea 84 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::__copy<false, std::random_access_iterator_tag>::copy<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>)
+1b1ea e 280 61
+1b1f8 17 283 61
+1b20f 20 285 61
+1b22f b 286 61
+1b23a b 287 61
+1b245 b 283 61
+1b250 1e 289 61
+FUNC 1b26e 7e 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::__copy_aux<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>)
+1b26e 11 307 79
+1b27f 4 315 61
+1b283 69 317 61
+FUNC 1b2ec 7a 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::__copy_normal<false, false>::copy_n<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>)
+1b2ec 11 325 61
+1b2fd 69 326 61
+FUNC 1b366 82 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::copy<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>)
+1b366 11 376 79
+1b377 4 384 61
+1b37b 4 385 61
+1b37f 69 387 61
+FUNC 1b3e8 7a 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::__uninitialized_copy_aux<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, __true_type)
+1b3e8 11 73 79
+1b3f9 69 74 79
+FUNC 1b462 82 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::uninitialized_copy<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> >(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>)
+1b462 11 108 79
+1b473 71 113 79
+FUNC 1b4e4 7a 0 std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*> std::__uninitialized_copy_a<std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, unsigned long long>(std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long const&, unsigned long long const*>, std::_Deque_iterator<unsigned long long, unsigned long long&, unsigned long long*>, std::allocator<unsigned long long>)
+1b4e4 11 252 79
+1b4f5 69 254 79
+1b55e 10 679 62
+1b56e 64 680 62
+1b5d2 e8 681 62
+1b6ba c 143 75
+1b6c6 14 144 75
+1b6da 6 144 75
+FUNC 1b6e0 4d 0 __eprintf
+1b6e0 6 1826 80
+1b6e6 3 1832 80
+1b6e9 c 1826 80
+1b6f5 29 1832 80
+1b71e a 1837 80
+1b728 5 1838 80
+1b72d e8d3 1838 80
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/ucontext_compat.h b/toolkit/crashreporter/breakpad-client/mac/handler/ucontext_compat.h
new file mode 100644
index 0000000000..1e4b752e51
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/ucontext_compat.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_
+#define CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_
+
+#include <sys/ucontext.h>
+
+// The purpose of this file is to work around the fact that ucontext_t's
+// uc_mcontext member is an mcontext_t rather than an mcontext64_t on ARM64.
+#if defined(__aarch64__)
+// <sys/ucontext.h> doesn't include the below file.
+#include <sys/_types/_ucontext64.h>
+typedef ucontext64_t breakpad_ucontext_t;
+#define breakpad_uc_mcontext uc_mcontext64
+#else
+typedef ucontext_t breakpad_ucontext_t;
+#define breakpad_uc_mcontext uc_mcontext
+#endif // defined(__aarch64__)
+
+#endif // CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/BreakpadFramework_Test.mm b/toolkit/crashreporter/breakpad-client/mac/tests/BreakpadFramework_Test.mm
new file mode 100644
index 0000000000..2ea103c694
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/BreakpadFramework_Test.mm
@@ -0,0 +1,217 @@
+// Copyright (c) 2009, 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.
+//
+// BreakpadFramework_Test.mm
+// Test case file for Breakpad.h/mm.
+//
+
+#import "GTMSenTestCase.h"
+#import "Breakpad.h"
+
+#include <mach/mach.h>
+
+@interface BreakpadFramework_Test : GTMTestCase {
+ @private
+ int last_exception_code_;
+ int last_exception_type_;
+ mach_port_t last_exception_thread_;
+ // We're not using Obj-C BOOL because we need to interop with
+ // Breakpad's callback.
+ bool shouldHandleException_;
+}
+
+// This method is used by a callback used by test cases to determine
+// whether to return true or false to Breakpad when handling an
+// exception.
+- (bool)shouldHandleException;
+// This method returns a minimal dictionary that has what
+// Breakpad needs to initialize.
+- (NSMutableDictionary *)breakpadInitializationDictionary;
+// This method is used by the exception handling callback
+// to communicate to test cases the properites of the last
+// exception.
+- (void)setLastExceptionType:(int)type andCode:(int)code
+ andThread:(mach_port_t)thread;
+@end
+
+// Callback for Breakpad exceptions
+bool myBreakpadCallback(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread,
+ void *context);
+
+bool myBreakpadCallback(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread,
+ void *context) {
+ BreakpadFramework_Test *testCaseClass =
+ (BreakpadFramework_Test *)context;
+ [testCaseClass setLastExceptionType:exception_type
+ andCode:exception_code
+ andThread:crashing_thread];
+ bool shouldHandleException =
+ [testCaseClass shouldHandleException];
+ NSLog(@"Callback returning %d", shouldHandleException);
+ return shouldHandleException;
+}
+const int kNoLastExceptionCode = -1;
+const int kNoLastExceptionType = -1;
+const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL;
+
+@implementation BreakpadFramework_Test
+- (void) initializeExceptionStateVariables {
+ last_exception_code_ = kNoLastExceptionCode;
+ last_exception_type_ = kNoLastExceptionType;
+ last_exception_thread_ = kNoLastExceptionThread;
+}
+
+- (NSMutableDictionary *)breakpadInitializationDictionary {
+ NSMutableDictionary *breakpadParams =
+ [NSMutableDictionary dictionaryWithCapacity:3];
+
+ [breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT];
+ [breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION];
+ [breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL];
+ return breakpadParams;
+}
+
+- (bool)shouldHandleException {
+ return shouldHandleException_;
+}
+
+- (void)setLastExceptionType:(int)type
+ andCode:(int)code
+ andThread:(mach_port_t)thread {
+ last_exception_type_ = type;
+ last_exception_code_ = code;
+ last_exception_thread_ = thread;
+}
+
+// Test that the parameters mark required actually enable Breakpad to
+// be initialized.
+- (void)testBreakpadInstantiationWithRequiredParameters {
+ BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]);
+ STAssertNotNULL(b, @"BreakpadCreate failed with required parameters");
+ BreakpadRelease(b);
+}
+
+// Test that Breakpad fails to initialize cleanly when required
+// parameters are not present.
+- (void)testBreakpadInstantiationWithoutRequiredParameters {
+ NSMutableDictionary *breakpadDictionary =
+ [self breakpadInitializationDictionary];
+
+ // Skip setting version, so that BreakpadCreate fails.
+ [breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION];
+ BreakpadRef b = BreakpadCreate(breakpadDictionary);
+ STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
+ " parameter!");
+
+ breakpadDictionary = [self breakpadInitializationDictionary];
+ // Now test with no product
+ [breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT];
+ b = BreakpadCreate(breakpadDictionary);
+ STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
+ " parameter!");
+
+ breakpadDictionary = [self breakpadInitializationDictionary];
+ // Now test with no URL
+ [breakpadDictionary removeObjectForKey:@BREAKPAD_URL];
+ b = BreakpadCreate(breakpadDictionary);
+ STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
+ " parameter!");
+ BreakpadRelease(b);
+}
+
+// Test to ensure that when we call BreakpadAddUploadParameter,
+// it's added to the dictionary correctly(this test depends on
+// some internal details of Breakpad, namely, the special prefix
+// that it uses to figure out which key/value pairs to upload).
+- (void)testAddingBreakpadServerVariable {
+ NSMutableDictionary *breakpadDictionary =
+ [self breakpadInitializationDictionary];
+
+ BreakpadRef b = BreakpadCreate(breakpadDictionary);
+ STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
+
+ BreakpadAddUploadParameter(b,
+ @"key",
+ @"value");
+
+ // Test that it did not add the key/value directly, e.g. without
+ // prepending the key with the prefix.
+ STAssertNil(BreakpadKeyValue(b, @"key"),
+ @"AddUploadParameter added key directly to dictionary"
+ " instead of prepending it!");
+
+ NSString *prependedKeyname =
+ [@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"];
+
+ STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname),
+ @"value",
+ @"Calling BreakpadAddUploadParameter did not prepend "
+ "key name");
+ BreakpadRelease(b);
+}
+
+// Test that when we do on-demand minidump generation,
+// the exception code/type/thread are set properly.
+- (void)testFilterCallbackReturnsFalse {
+ NSMutableDictionary *breakpadDictionary =
+ [self breakpadInitializationDictionary];
+
+ BreakpadRef b = BreakpadCreate(breakpadDictionary);
+ STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
+ BreakpadSetFilterCallback(b, &myBreakpadCallback, self);
+
+ // This causes the callback to return false, meaning
+ // Breakpad won't take the exception
+ shouldHandleException_ = false;
+
+ [self initializeExceptionStateVariables];
+ STAssertEquals(last_exception_type_, kNoLastExceptionType,
+ @"Last exception type not initialized correctly.");
+ STAssertEquals(last_exception_code_, kNoLastExceptionCode,
+ @"Last exception code not initialized correctly.");
+ STAssertEquals(last_exception_thread_, kNoLastExceptionThread,
+ @"Last exception thread is not initialized correctly.");
+
+ // Cause Breakpad's exception handler to be invoked.
+ BreakpadGenerateAndSendReport(b);
+
+ STAssertEquals(last_exception_type_, 0,
+ @"Last exception type is not 0 for on demand");
+ STAssertEquals(last_exception_code_, 0,
+ @"Last exception code is not 0 for on demand");
+ STAssertEquals(last_exception_thread_, mach_thread_self(),
+ @"Last exception thread is not mach_thread_self() "
+ "for on demand");
+}
+
+@end
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/crash_generation_server_test.cc b/toolkit/crashreporter/breakpad-client/mac/tests/crash_generation_server_test.cc
new file mode 100644
index 0000000000..0164f4a298
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/crash_generation_server_test.cc
@@ -0,0 +1,398 @@
+// 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.
+//
+// crash_generation_server_test.cc
+// Unit tests for CrashGenerationServer
+
+#include <dirent.h>
+#include <glob.h>
+#include <stdint.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "client/mac/crash_generation/client_info.h"
+#include "client/mac/crash_generation/crash_generation_client.h"
+#include "client/mac/crash_generation/crash_generation_server.h"
+#include "client/mac/handler/exception_handler.h"
+#include "client/mac/tests/spawn_child_process.h"
+#include "common/tests/auto_tempdir.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace google_breakpad {
+// This acts as the log sink for INFO logging from the processor
+// logging code. The logging output confuses XCode and makes it think
+// there are unit test failures. testlogging.h handles the overriding.
+std::ostringstream info_log;
+}
+
+namespace {
+using std::string;
+using google_breakpad::AutoTempDir;
+using google_breakpad::ClientInfo;
+using google_breakpad::CrashGenerationClient;
+using google_breakpad::CrashGenerationServer;
+using google_breakpad::ExceptionHandler;
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpModule;
+using google_breakpad::MinidumpModuleList;
+using google_breakpad::MinidumpSystemInfo;
+using google_breakpad::MinidumpThread;
+using google_breakpad::MinidumpThreadList;
+using testing::Test;
+using namespace google_breakpad_test;
+
+class CrashGenerationServerTest : public Test {
+public:
+ // The port name to receive messages on
+ char mach_port_name[128];
+ // Filename of the last dump that was generated
+ string last_dump_name;
+ // PID of the child process
+ pid_t child_pid;
+ // A temp dir
+ AutoTempDir temp_dir;
+ // Counter just to ensure that we don't hit the same port again
+ static int i;
+ bool filter_callback_called;
+
+ void SetUp() {
+ sprintf(mach_port_name,
+ "com.google.breakpad.ServerTest.%d.%d", getpid(),
+ CrashGenerationServerTest::i++);
+ child_pid = (pid_t)-1;
+ filter_callback_called = false;
+ }
+};
+int CrashGenerationServerTest::i = 0;
+
+// Test that starting and stopping a server works
+TEST_F(CrashGenerationServerTest, testStartStopServer) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // filter callback
+ NULL, // filter context
+ NULL, // dump callback
+ NULL, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ false, // generate dumps
+ ""); // dump path
+ ASSERT_TRUE(server.Start());
+ ASSERT_TRUE(server.Stop());
+}
+
+// Test that requesting a dump via CrashGenerationClient works
+// Test without actually dumping
+TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // filter callback
+ NULL, // filter context
+ NULL, // dump callback
+ NULL, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ false, // don't generate dumps
+ temp_dir.path()); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ CrashGenerationClient client(mach_port_name);
+ bool result = client.RequestDump();
+ exit(result ? 0 : 1);
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_TRUE(WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that no minidump was written
+ string pattern = temp_dir.path() + "/*";
+ glob_t dirContents;
+ ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
+ EXPECT_EQ(GLOB_NOMATCH, ret);
+ if (ret != GLOB_NOMATCH)
+ globfree(&dirContents);
+}
+
+void dumpCallback(void *context, const ClientInfo &client_info,
+ const std::string &file_path) {
+ if (context) {
+ CrashGenerationServerTest* self =
+ reinterpret_cast<CrashGenerationServerTest*>(context);
+ if (!file_path.empty())
+ self->last_dump_name = file_path;
+ self->child_pid = client_info.pid();
+ }
+}
+
+void *RequestDump(void *context) {
+ CrashGenerationClient client((const char*)context);
+ bool result = client.RequestDump();
+ return (void*)(result ? 0 : 1);
+}
+
+// Test that actually writing a minidump works
+TEST_F(CrashGenerationServerTest, testRequestDump) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // filter callback
+ NULL, // filter context
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path()); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Have to spawn off a separate thread to request the dump,
+ // because MinidumpGenerator assumes the handler thread is not
+ // the only thread
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
+ exit(1);
+ void* result;
+ pthread_join(thread, &result);
+ exit(reinterpret_cast<intptr_t>(result));
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_TRUE(WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that minidump was written
+ ASSERT_FALSE(last_dump_name.empty());
+ struct stat st;
+ EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
+ EXPECT_LT(0, st.st_size);
+ // check client's PID
+ ASSERT_EQ(pid, child_pid);
+}
+
+static void Crasher() {
+ int *a = (int*)0x42;
+
+ fprintf(stdout, "Going to crash...\n");
+ fprintf(stdout, "A = %d", *a);
+}
+
+// Test that crashing a child process with an OOP ExceptionHandler installed
+// results in a minidump being written by the CrashGenerationServer in
+// the parent.
+TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // filter callback
+ NULL, // filter context
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path()); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Instantiate an OOP exception handler.
+ ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
+ Crasher();
+ // not reached
+ exit(0);
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_FALSE(WIFEXITED(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that minidump was written
+ ASSERT_FALSE(last_dump_name.empty());
+ struct stat st;
+ EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
+ EXPECT_LT(0, st.st_size);
+
+ // Read the minidump, sanity check some data.
+ Minidump minidump(last_dump_name.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(system_info);
+ const MDRawSystemInfo* raw_info = system_info->system_info();
+ ASSERT_TRUE(raw_info);
+ EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list);
+ ASSERT_EQ((unsigned int)1, thread_list->thread_count());
+
+ MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(main_thread);
+ MinidumpContext* context = main_thread->GetContext();
+ ASSERT_TRUE(context);
+ EXPECT_EQ(kNativeContext, context->GetContextCPU());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* main_module = module_list->GetMainModule();
+ ASSERT_TRUE(main_module);
+ EXPECT_EQ(GetExecutablePath(), main_module->code_file());
+}
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
+ (defined(__x86_64__) || defined(__i386__))
+// Test that crashing a child process of a different architecture
+// produces a valid minidump.
+TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // filter callback
+ NULL, // filter context
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path()); // dump path
+ ASSERT_TRUE(server.Start());
+
+ // Spawn a child process
+ string helper_path = GetHelperPath();
+ const char* argv[] = {
+ helper_path.c_str(),
+ "crash",
+ mach_port_name,
+ NULL
+ };
+ pid_t pid = spawn_child_process(argv);
+ ASSERT_NE(-1, pid);
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_FALSE(WIFEXITED(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that minidump was written
+ ASSERT_FALSE(last_dump_name.empty());
+ struct stat st;
+ EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
+ EXPECT_LT(0, st.st_size);
+
+const MDCPUArchitecture kExpectedArchitecture =
+#if defined(__x86_64__)
+ MD_CPU_ARCHITECTURE_X86
+#elif defined(__i386__)
+ MD_CPU_ARCHITECTURE_AMD64
+#endif
+ ;
+const uint32_t kExpectedContext =
+#if defined(__i386__)
+ MD_CONTEXT_AMD64
+#elif defined(__x86_64__)
+ MD_CONTEXT_X86
+#endif
+ ;
+
+ // Read the minidump, sanity check some data.
+ Minidump minidump(last_dump_name.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(system_info);
+ const MDRawSystemInfo* raw_info = system_info->system_info();
+ ASSERT_TRUE(raw_info);
+ EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list);
+ ASSERT_EQ((unsigned int)1, thread_list->thread_count());
+
+ MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(main_thread);
+ MinidumpContext* context = main_thread->GetContext();
+ ASSERT_TRUE(context);
+ EXPECT_EQ(kExpectedContext, context->GetContextCPU());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* main_module = module_list->GetMainModule();
+ ASSERT_TRUE(main_module);
+ EXPECT_EQ(helper_path, main_module->code_file());
+}
+#endif
+
+bool filter_callback(void* context) {
+ CrashGenerationServerTest* self =
+ reinterpret_cast<CrashGenerationServerTest*>(context);
+ self->filter_callback_called = true;
+ // veto dump generation
+ return false;
+}
+
+// Test that a filter callback can veto minidump writing.
+TEST_F(CrashGenerationServerTest, testFilter) {
+ CrashGenerationServer server(mach_port_name,
+ filter_callback, // filter callback
+ this, // filter context
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path()); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Instantiate an OOP exception handler.
+ ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
+ Crasher();
+ // not reached
+ exit(0);
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_FALSE(WIFEXITED(ret));
+ EXPECT_TRUE(server.Stop());
+
+ // check that no minidump was written
+ EXPECT_TRUE(last_dump_name.empty());
+ EXPECT_TRUE(filter_callback_called);
+}
+
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/exception_handler_test.cc b/toolkit/crashreporter/breakpad-client/mac/tests/exception_handler_test.cc
new file mode 100644
index 0000000000..d5b505a1e1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/exception_handler_test.cc
@@ -0,0 +1,714 @@
+// 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.
+
+// exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
+
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "breakpad_googletest_includes.h"
+#include "client/mac/handler/exception_handler.h"
+#include "common/linux/ignore_ret.h"
+#include "common/mac/MachIPC.h"
+#include "common/tests/auto_tempdir.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace google_breakpad {
+// This acts as the log sink for INFO logging from the processor
+// logging code. The logging output confuses XCode and makes it think
+// there are unit test failures. testlogging.h handles the overriding.
+std::ostringstream info_log;
+}
+
+namespace {
+using std::string;
+using google_breakpad::AutoTempDir;
+using google_breakpad::ExceptionHandler;
+using google_breakpad::MachPortSender;
+using google_breakpad::MachReceiveMessage;
+using google_breakpad::MachSendMessage;
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpMemoryList;
+using google_breakpad::MinidumpMemoryRegion;
+using google_breakpad::ReceivePort;
+using testing::Test;
+
+class ExceptionHandlerTest : public Test {
+ public:
+ void InProcessCrash(bool aborting);
+ AutoTempDir tempDir;
+ string lastDumpName;
+};
+
+static void Crasher() {
+ int *a = (int*)0x42;
+
+ fprintf(stdout, "Going to crash...\n");
+ fprintf(stdout, "A = %d", *a);
+}
+
+static void AbortCrasher() {
+ fprintf(stdout, "Going to crash...\n");
+ abort();
+}
+
+static void SoonToCrash(void(*crasher)()) {
+ crasher();
+}
+
+static bool MDCallback(const char *dump_dir, const char *file_name,
+ void *context, bool success) {
+ string path(dump_dir);
+ path.append("/");
+ path.append(file_name);
+ path.append(".dmp");
+
+ int fd = *reinterpret_cast<int*>(context);
+ IGNORE_RET(write(fd, path.c_str(), path.length() + 1));
+ close(fd);
+ exit(0);
+ // not reached
+ return true;
+}
+
+void ExceptionHandlerTest::InProcessCrash(bool aborting) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+ // Fork off a child process so it can crash.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // In the child process.
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+ // crash
+ SoonToCrash(aborting ? &AbortCrasher : &Crasher);
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+
+ const MDRawExceptionStream* raw_exception = exception->exception();
+ ASSERT_TRUE(raw_exception);
+
+ if (aborting) {
+ EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE,
+ raw_exception->exception_record.exception_code);
+ EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT,
+ raw_exception->exception_record.exception_flags);
+ } else {
+ EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS,
+ raw_exception->exception_record.exception_code);
+#if defined(__x86_64__)
+ EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS,
+ raw_exception->exception_record.exception_flags);
+#elif defined(__i386__)
+ EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE,
+ raw_exception->exception_record.exception_flags);
+#endif
+ }
+
+ const MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ // Ideally would like to sanity check that abort() is on the stack
+ // but that's hard.
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(memory_list);
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ EXPECT_TRUE(region);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+}
+
+TEST_F(ExceptionHandlerTest, InProcess) {
+ InProcessCrash(false);
+}
+
+TEST_F(ExceptionHandlerTest, InProcessAbort) {
+ InProcessCrash(true);
+}
+
+static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
+ void *context, bool success) {
+ ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
+ if (dump_dir && file_name) {
+ self->lastDumpName = dump_dir;
+ self->lastDumpName += "/";
+ self->lastDumpName += file_name;
+ self->lastDumpName += ".dmp";
+ }
+ return true;
+}
+
+TEST_F(ExceptionHandlerTest, WriteMinidump) {
+ ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
+ NULL);
+ ASSERT_TRUE(eh.WriteMinidump());
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ ASSERT_FALSE(lastDumpName.empty());
+ struct stat st;
+ ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // The minidump should not contain an exception stream.
+ Minidump minidump(lastDumpName);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ EXPECT_FALSE(exception);
+}
+
+TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
+ ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
+ NULL);
+ ASSERT_TRUE(eh.WriteMinidump(true));
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ ASSERT_FALSE(lastDumpName.empty());
+ struct stat st;
+ ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // The minidump should contain an exception stream.
+ Minidump minidump(lastDumpName);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+ const MDRawExceptionStream* raw_exception = exception->exception();
+ ASSERT_TRUE(raw_exception);
+
+ EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT,
+ raw_exception->exception_record.exception_code);
+}
+
+TEST_F(ExceptionHandlerTest, DumpChildProcess) {
+ const int kTimeoutMs = 2000;
+ // Create a mach port to receive the child task on.
+ char machPortName[128];
+ sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
+ ReceivePort parent_recv_port(machPortName);
+
+ // Give the child process a pipe to block on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // Fork off a child process to dump.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // In the child process
+ close(fds[1]);
+
+ // Send parent process the task and thread ports.
+ MachSendMessage child_message(0);
+ child_message.AddDescriptor(mach_task_self());
+ child_message.AddDescriptor(mach_thread_self());
+
+ MachPortSender child_sender(machPortName);
+ if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
+ exit(1);
+
+ // Wait for the parent process.
+ uint8_t data;
+ read(fds[0], &data, 1);
+ exit(0);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[0]);
+
+ // Read the child's task and thread ports.
+ MachReceiveMessage child_message;
+ ASSERT_EQ(KERN_SUCCESS,
+ parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
+ mach_port_t child_task = child_message.GetTranslatedPort(0);
+ mach_port_t child_thread = child_message.GetTranslatedPort(1);
+ ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
+ ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
+
+ // Write a minidump of the child process.
+ bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
+ child_thread,
+ tempDir.path(),
+ DumpNameMDCallback,
+ this);
+ ASSERT_EQ(true, result);
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ ASSERT_FALSE(lastDumpName.empty());
+ struct stat st;
+ ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Unblock child process
+ uint8_t data = 1;
+ IGNORE_RET(write(fds[1], &data, 1));
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+}
+
+// Test that memory around the instruction pointer is written
+// to the dump as a MinidumpMemoryRegion.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = 256; // bytes
+ const int kOffset = kMemorySize / 2;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them in the middle
+ // of the block of memory, because the minidump should contain 128
+ // bytes on either side of the instruction pointer.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_file, &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ EXPECT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t prefix_bytes[kOffset];
+ uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the low end.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const uint32_t kMemorySize = 256; // bytes
+ const int kOffset = 0;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them at the start
+ // of the block of memory, to ensure that the memory bounding
+ // works properly.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_file, &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ EXPECT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize / 2, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the high end.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ // Use 4k here because the OS will hand out a single page even
+ // if a smaller size is requested, and this test wants to
+ // test the upper bound of the memory range.
+ const uint32_t kMemorySize = 4096; // bytes
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ const int kOffset = kMemorySize - sizeof(instructions);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+ // Get some executable memory.
+ char* memory =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMemorySize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0));
+ if (!memory)
+ exit(0);
+
+ // Write some instructions that will crash. Put them at the start
+ // of the block of memory, to ensure that the memory bounding
+ // works properly.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ memory_function();
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_file, &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ EXPECT_TRUE(region);
+
+ const size_t kPrefixSize = 128; // bytes
+ EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t prefix_bytes[kPrefixSize];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kPrefixSize,
+ instructions, sizeof(instructions)) == 0);
+}
+
+// Ensure that an extra memory block doesn't get added when the
+// instruction pointer is not in mapped memory.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+ // Try calling a NULL pointer.
+ typedef void (*void_function)(void);
+ // Volatile markings are needed to keep Clang from generating invalid
+ // opcodes. See http://crbug.com/498354 for details.
+ volatile void_function memory_function =
+ reinterpret_cast<void_function>(NULL);
+ memory_function();
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_file, &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is only one memory region
+ // in the memory list (the thread memory from the single thread).
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_EQ((unsigned int)1, memory_list->region_count());
+}
+
+static void *Junk(void *) {
+ sleep(1000000);
+ return NULL;
+}
+
+// Test that the memory list gets written correctly when multiple
+// threads are running.
+TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
+ // Give the child process a pipe to report back on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(fds[0]);
+ ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
+
+ // Run an extra thread so >2 memory regions will be written.
+ pthread_t junk_thread;
+ if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0)
+ pthread_detach(junk_thread);
+
+ // Just crash.
+ Crasher();
+
+ // not reached
+ exit(1);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Wait for the background process to return the minidump file.
+ close(fds[1]);
+ char minidump_file[PATH_MAX];
+ ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+ ASSERT_NE(0, nbytes);
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(minidump_file, &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump, and verify that the memory list can be read.
+ Minidump minidump(minidump_file);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(memory_list);
+ // Verify that there are three memory regions:
+ // one per thread, and one for the instruction pointer memory.
+ ASSERT_EQ((unsigned int)3, memory_list->region_count());
+}
+
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test.cc b/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test.cc
new file mode 100644
index 0000000000..b1fa5d02a1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test.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.
+
+// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
+
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_6
+#define MAC_OS_X_VERSION_10_6 1060
+#endif
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "client/mac/handler/minidump_generator.h"
+#include "client/mac/tests/spawn_child_process.h"
+#include "common/linux/ignore_ret.h"
+#include "common/mac/MachIPC.h"
+#include "common/tests/auto_tempdir.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace google_breakpad {
+// This acts as the log sink for INFO logging from the processor
+// logging code. The logging output confuses XCode and makes it think
+// there are unit test failures. testlogging.h handles the overriding.
+std::ostringstream info_log;
+}
+
+namespace {
+using std::string;
+using std::vector;
+using google_breakpad::AutoTempDir;
+using google_breakpad::MinidumpGenerator;
+using google_breakpad::MachPortSender;
+using google_breakpad::MachReceiveMessage;
+using google_breakpad::MachSendMessage;
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpModule;
+using google_breakpad::MinidumpModuleList;
+using google_breakpad::MinidumpSystemInfo;
+using google_breakpad::MinidumpThread;
+using google_breakpad::MinidumpThreadList;
+using google_breakpad::ReceivePort;
+using testing::Test;
+using namespace google_breakpad_test;
+
+class MinidumpGeneratorTest : public Test {
+ public:
+ AutoTempDir tempDir;
+};
+
+static void *Junk(void* data) {
+ bool* wait = reinterpret_cast<bool*>(data);
+ while (!*wait) {
+ usleep(10000);
+ }
+ return NULL;
+}
+
+TEST_F(MinidumpGeneratorTest, InProcess) {
+ MinidumpGenerator generator;
+ string dump_filename =
+ MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
+
+ // Run an extra thread since MinidumpGenerator assumes there
+ // are 2 or more threads.
+ pthread_t junk_thread;
+ bool quit = false;
+ ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
+
+ ASSERT_TRUE(generator.Write(dump_filename.c_str()));
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // join the background thread
+ quit = true;
+ pthread_join(junk_thread, NULL);
+
+ // Read the minidump, sanity check some data.
+ Minidump minidump(dump_filename.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(system_info);
+ const MDRawSystemInfo* raw_info = system_info->system_info();
+ ASSERT_TRUE(raw_info);
+ EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list);
+ ASSERT_EQ((unsigned int)1, thread_list->thread_count());
+
+ MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(main_thread);
+ MinidumpContext* context = main_thread->GetContext();
+ ASSERT_TRUE(context);
+ EXPECT_EQ(kNativeContext, context->GetContextCPU());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* main_module = module_list->GetMainModule();
+ ASSERT_TRUE(main_module);
+ EXPECT_EQ(GetExecutablePath(), main_module->code_file());
+}
+
+TEST_F(MinidumpGeneratorTest, OutOfProcess) {
+ const int kTimeoutMs = 2000;
+ // Create a mach port to receive the child task on.
+ char machPortName[128];
+ sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
+ ReceivePort parent_recv_port(machPortName);
+
+ // Give the child process a pipe to block on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // Fork off a child process to dump.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // In the child process
+ close(fds[1]);
+
+ // Send parent process the task port.
+ MachSendMessage child_message(0);
+ child_message.AddDescriptor(mach_task_self());
+
+ MachPortSender child_sender(machPortName);
+ if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
+ fprintf(stderr, "Error sending message from child process!\n");
+ exit(1);
+ }
+
+ // Wait for the parent process.
+ uint8_t data;
+ read(fds[0], &data, 1);
+ exit(0);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[0]);
+
+ // Read the child's task port.
+ MachReceiveMessage child_message;
+ ASSERT_EQ(KERN_SUCCESS,
+ parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
+ mach_port_t child_task = child_message.GetTranslatedPort(0);
+ ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
+
+ // Write a minidump of the child process.
+ MinidumpGenerator generator(child_task, MACH_PORT_NULL);
+ string dump_filename =
+ MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
+ ASSERT_TRUE(generator.Write(dump_filename.c_str()));
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Unblock child process
+ uint8_t data = 1;
+ IGNORE_RET(write(fds[1], &data, 1));
+
+ // Child process should have exited with a zero status.
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_NE(0, WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+
+ // Read the minidump, sanity check some data.
+ Minidump minidump(dump_filename.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(system_info);
+ const MDRawSystemInfo* raw_info = system_info->system_info();
+ ASSERT_TRUE(raw_info);
+ EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list);
+ ASSERT_EQ((unsigned int)1, thread_list->thread_count());
+
+ MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(main_thread);
+ MinidumpContext* context = main_thread->GetContext();
+ ASSERT_TRUE(context);
+ EXPECT_EQ(kNativeContext, context->GetContextCPU());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* main_module = module_list->GetMainModule();
+ ASSERT_TRUE(main_module);
+ EXPECT_EQ(GetExecutablePath(), main_module->code_file());
+}
+
+// This test fails on 10.5, but I don't have easy access to a 10.5 machine,
+// so it's simpler to just limit it to 10.6 for now.
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
+ (defined(__x86_64__) || defined(__i386__))
+
+TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
+ const int kTimeoutMs = 5000;
+ // Create a mach port to receive the child task on.
+ char machPortName[128];
+ sprintf(machPortName,
+ "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
+
+ ReceivePort parent_recv_port(machPortName);
+
+ // Spawn a child process to dump.
+ string helper_path = GetHelperPath();
+ const char* argv[] = {
+ helper_path.c_str(),
+ machPortName,
+ NULL
+ };
+ pid_t pid = spawn_child_process(argv);
+ ASSERT_NE(-1, pid);
+
+ // Read the child's task port.
+ MachReceiveMessage child_message;
+ ASSERT_EQ(KERN_SUCCESS,
+ parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
+ mach_port_t child_task = child_message.GetTranslatedPort(0);
+ ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
+
+ // Write a minidump of the child process.
+ MinidumpGenerator generator(child_task, MACH_PORT_NULL);
+ string dump_filename =
+ MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
+ ASSERT_TRUE(generator.Write(dump_filename.c_str()));
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ struct stat st;
+ ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Kill child process.
+ kill(pid, SIGKILL);
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+
+const MDCPUArchitecture kExpectedArchitecture =
+#if defined(__x86_64__)
+ MD_CPU_ARCHITECTURE_X86
+#elif defined(__i386__)
+ MD_CPU_ARCHITECTURE_AMD64
+#endif
+ ;
+const uint32_t kExpectedContext =
+#if defined(__i386__)
+ MD_CONTEXT_AMD64
+#elif defined(__x86_64__)
+ MD_CONTEXT_X86
+#endif
+ ;
+
+ // Read the minidump, sanity check some data.
+ Minidump minidump(dump_filename.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
+ ASSERT_TRUE(system_info);
+ const MDRawSystemInfo* raw_info = system_info->system_info();
+ ASSERT_TRUE(raw_info);
+ EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list);
+ ASSERT_EQ((unsigned int)1, thread_list->thread_count());
+
+ MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(main_thread);
+ MinidumpContext* context = main_thread->GetContext();
+ ASSERT_TRUE(context);
+ EXPECT_EQ(kExpectedContext, context->GetContextCPU());
+
+ MinidumpModuleList* module_list = minidump.GetModuleList();
+ ASSERT_TRUE(module_list);
+ const MinidumpModule* main_module = module_list->GetMainModule();
+ ASSERT_TRUE(main_module);
+ EXPECT_EQ(helper_path, main_module->code_file());
+}
+#endif // 10.6 && (x86-64 || i386)
+
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test_helper.cc b/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test_helper.cc
new file mode 100644
index 0000000000..4e8ce3cf00
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/minidump_generator_test_helper.cc
@@ -0,0 +1,74 @@
+// 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_generator_test_helper.cc: A helper program that
+// minidump_generator_test.cc can launch to test certain things
+// that require a separate executable.
+
+#include <unistd.h>
+
+#include "client/mac/handler/exception_handler.h"
+#include "common/mac/MachIPC.h"
+
+using google_breakpad::MachPortSender;
+using google_breakpad::MachReceiveMessage;
+using google_breakpad::MachSendMessage;
+using google_breakpad::ReceivePort;
+
+int main(int argc, char** argv) {
+ if (argc < 2)
+ return 1;
+
+ if (strcmp(argv[1], "crash") != 0) {
+ const int kTimeoutMs = 2000;
+ // Send parent process the task and thread ports.
+ MachSendMessage child_message(0);
+ child_message.AddDescriptor(mach_task_self());
+ child_message.AddDescriptor(mach_thread_self());
+
+ MachPortSender child_sender(argv[1]);
+ if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
+ fprintf(stderr, "Error sending message from child process!\n");
+ exit(1);
+ }
+
+ // Loop forever.
+ while (true) {
+ sleep(100);
+ }
+ } else if (argc == 3 && strcmp(argv[1], "crash") == 0) {
+ // Instantiate an OOP exception handler
+ google_breakpad::ExceptionHandler eh("", NULL, NULL, NULL, true, argv[2]);
+ // and crash.
+ int *a = (int*)0x42;
+ *a = 1;
+ }
+
+ return 0;
+}
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/spawn_child_process.h b/toolkit/crashreporter/breakpad-client/mac/tests/spawn_child_process.h
new file mode 100644
index 0000000000..1b82c56278
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/spawn_child_process.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utility functions for spawning a helper process using a different
+// CPU architecture.
+
+#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS
+#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS
+
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_6
+#define MAC_OS_X_VERSION_10_6 1060
+#endif
+#include <crt_externs.h>
+#include <mach-o/dyld.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
+#include <spawn.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad_test {
+
+using std::string;
+using std::vector;
+
+const MDCPUArchitecture kNativeArchitecture =
+#if defined(__i386__)
+ MD_CPU_ARCHITECTURE_X86
+#elif defined(__x86_64__)
+ MD_CPU_ARCHITECTURE_AMD64
+#elif defined(__ppc__) || defined(__ppc64__)
+ MD_CPU_ARCHITECTURE_PPC
+#else
+#error "This file has not been ported to this CPU architecture."
+#endif
+ ;
+
+const uint32_t kNativeContext =
+#if defined(__i386__)
+ MD_CONTEXT_X86
+#elif defined(__x86_64__)
+ MD_CONTEXT_AMD64
+#elif defined(__ppc__) || defined(__ppc64__)
+ MD_CONTEXT_PPC
+#else
+#error "This file has not been ported to this CPU architecture."
+#endif
+ ;
+
+string GetExecutablePath() {
+ char self_path[PATH_MAX];
+ uint32_t size = sizeof(self_path);
+ if (_NSGetExecutablePath(self_path, &size) != 0)
+ return "";
+ return self_path;
+}
+
+string GetHelperPath() {
+ string helper_path(GetExecutablePath());
+ size_t pos = helper_path.rfind('/');
+ if (pos == string::npos)
+ return "";
+
+ helper_path.erase(pos + 1);
+ helper_path += "minidump_generator_test_helper";
+ return helper_path;
+}
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
+
+pid_t spawn_child_process(const char** argv) {
+ posix_spawnattr_t spawnattr;
+ if (posix_spawnattr_init(&spawnattr) != 0)
+ return (pid_t)-1;
+
+ cpu_type_t pref_cpu_types[2] = {
+#if defined(__x86_64__)
+ CPU_TYPE_X86,
+#elif defined(__i386__)
+ CPU_TYPE_X86_64,
+#endif
+ CPU_TYPE_ANY
+ };
+
+ // Set spawn attributes.
+ size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]);
+ size_t attr_ocount = 0;
+ if (posix_spawnattr_setbinpref_np(&spawnattr,
+ attr_count,
+ pref_cpu_types,
+ &attr_ocount) != 0 ||
+ attr_ocount != attr_count) {
+ posix_spawnattr_destroy(&spawnattr);
+ return (pid_t)-1;
+ }
+
+ // Create an argv array.
+ vector<char*> argv_v;
+ while (*argv) {
+ argv_v.push_back(strdup(*argv));
+ argv++;
+ }
+ argv_v.push_back(NULL);
+ pid_t new_pid = 0;
+ int result = posix_spawnp(&new_pid, argv_v[0], NULL, &spawnattr,
+ &argv_v[0], *_NSGetEnviron());
+ posix_spawnattr_destroy(&spawnattr);
+
+ for (unsigned i = 0; i < argv_v.size(); i++) {
+ free(argv_v[i]);
+ }
+
+ return result == 0 ? new_pid : -1;
+}
+#endif
+
+} // namespace google_breakpad_test
+
+#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS
diff --git a/toolkit/crashreporter/breakpad-client/mac/tests/testlogging.h b/toolkit/crashreporter/breakpad-client/mac/tests/testlogging.h
new file mode 100644
index 0000000000..c6b6be699b
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/mac/tests/testlogging.h
@@ -0,0 +1,9 @@
+// This file exists to override the processor logging for unit tests,
+// since it confuses XCode into thinking unit tests have failed.
+#include <sstream>
+
+namespace google_breakpad {
+extern std::ostringstream info_log;
+}
+
+#define BPLOG_INFO_STREAM google_breakpad::info_log
diff --git a/toolkit/crashreporter/breakpad-client/minidump_file_writer-inl.h b/toolkit/crashreporter/breakpad-client/minidump_file_writer-inl.h
new file mode 100644
index 0000000000..bf63135b85
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/minidump_file_writer-inl.h
@@ -0,0 +1,97 @@
+// 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_file_writer-inl.h: Minidump file writer implementation.
+//
+// See minidump_file_writer.h for documentation.
+
+#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__
+#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__
+
+#include <assert.h>
+
+#include "minidump_file_writer.h"
+#include "google_breakpad/common/minidump_size.h"
+
+namespace google_breakpad {
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::Allocate() {
+ allocation_state_ = SINGLE_OBJECT;
+ return UntypedMDRVA::Allocate(minidump_size<MDType>::size());
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::Allocate(size_t additional) {
+ allocation_state_ = SINGLE_OBJECT;
+ return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional);
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
+ assert(count);
+ allocation_state_ = ARRAY;
+ return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count);
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count,
+ size_t length) {
+ assert(count && length);
+ allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
+ return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length);
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
+ assert(allocation_state_ == ARRAY);
+ return writer_->Copy(
+ static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),
+ item, minidump_size<MDType>::size());
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
+ const void *src,
+ size_t length) {
+ assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
+ return writer_->Copy(
+ static_cast<MDRVA>(position_ + minidump_size<MDType>::size()
+ + index * length),
+ src, length);
+}
+
+template<typename MDType>
+inline bool TypedMDRVA<MDType>::Flush() {
+ return writer_->Copy(position_, &data_, minidump_size<MDType>::size());
+}
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__
diff --git a/toolkit/crashreporter/breakpad-client/minidump_file_writer.cc b/toolkit/crashreporter/breakpad-client/minidump_file_writer.cc
new file mode 100644
index 0000000000..ed9e957d39
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/minidump_file_writer.cc
@@ -0,0 +1,402 @@
+// 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_file_writer.cc: Minidump file writer implementation.
+//
+// See minidump_file_writer.h for documentation.
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "minidump_file_writer-inl.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/string_conversion.h"
+#if defined(__linux__) && __linux__
+#include "third_party/lss/linux_syscall_support.h"
+#endif
+
+#if defined(__ANDROID__)
+#include <errno.h>
+
+namespace {
+
+bool g_need_ftruncate_workaround = false;
+bool g_checked_need_ftruncate_workaround = false;
+
+void CheckNeedsFTruncateWorkAround(int file) {
+ if (g_checked_need_ftruncate_workaround) {
+ return;
+ }
+ g_checked_need_ftruncate_workaround = true;
+
+ // Attempt an idempotent truncate that chops off nothing and see if we
+ // run into any sort of errors.
+ off_t offset = sys_lseek(file, 0, SEEK_END);
+ if (offset == -1) {
+ // lseek failed. Don't apply work around. It's unlikely that we can write
+ // to a minidump with either method.
+ return;
+ }
+
+ int result = ftruncate(file, offset);
+ if (result == -1 && errno == EACCES) {
+ // It very likely that we are running into the kernel bug in M devices.
+ // We are going to deploy the workaround for writing minidump files
+ // without uses of ftruncate(). This workaround should be fine even
+ // for kernels without the bug.
+ // See http://crbug.com/542840 for more details.
+ g_need_ftruncate_workaround = true;
+ }
+}
+
+bool NeedsFTruncateWorkAround() {
+ return g_need_ftruncate_workaround;
+}
+
+} // namespace
+#endif // defined(__ANDROID__)
+
+namespace google_breakpad {
+
+const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
+
+MinidumpFileWriter::MinidumpFileWriter()
+ : file_(-1),
+ close_file_when_destroyed_(true),
+ position_(0),
+ size_(0) {
+}
+
+MinidumpFileWriter::~MinidumpFileWriter() {
+ if (close_file_when_destroyed_)
+ Close();
+}
+
+bool MinidumpFileWriter::Open(const char *path) {
+ assert(file_ == -1);
+#if defined(__linux__) && __linux__
+ file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+#else
+ file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+#endif
+
+ return file_ != -1;
+}
+
+void MinidumpFileWriter::SetFile(const int file) {
+ assert(file_ == -1);
+ file_ = file;
+ close_file_when_destroyed_ = false;
+#if defined(__ANDROID__)
+ CheckNeedsFTruncateWorkAround(file);
+#endif
+}
+
+bool MinidumpFileWriter::Close() {
+ bool result = true;
+
+ if (file_ != -1) {
+#if defined(__ANDROID__)
+ if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
+ return false;
+ }
+#else
+ if (ftruncate(file_, position_)) {
+ return false;
+ }
+#endif
+#if defined(__linux__) && __linux__
+ result = (sys_close(file_) == 0);
+#else
+ result = (close(file_) == 0);
+#endif
+ file_ = -1;
+ }
+
+ return result;
+}
+
+bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
+ unsigned int length,
+ TypedMDRVA<MDString> *mdstring) {
+ bool result = true;
+ if (sizeof(wchar_t) == sizeof(uint16_t)) {
+ // Shortcut if wchar_t is the same size as MDString's buffer
+ result = mdstring->Copy(str, mdstring->get()->length);
+ } else {
+ uint16_t out[2];
+ int out_idx = 0;
+
+ // Copy the string character by character
+ while (length && result) {
+ UTF32ToUTF16Char(*str, out);
+ if (!out[0])
+ return false;
+
+ // Process one character at a time
+ --length;
+ ++str;
+
+ // Append the one or two UTF-16 characters. The first one will be non-
+ // zero, but the second one may be zero, depending on the conversion from
+ // UTF-32.
+ int out_count = out[1] ? 2 : 1;
+ size_t out_size = sizeof(uint16_t) * out_count;
+ result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
+ out_idx += out_count;
+ }
+ }
+ return result;
+}
+
+bool MinidumpFileWriter::CopyStringToMDString(const char *str,
+ unsigned int length,
+ TypedMDRVA<MDString> *mdstring) {
+ bool result = true;
+ uint16_t out[2];
+ int out_idx = 0;
+
+ // Copy the string character by character
+ while (length && result) {
+ int conversion_count = UTF8ToUTF16Char(str, length, out);
+ if (!conversion_count)
+ return false;
+
+ // Move the pointer along based on the nubmer of converted characters
+ length -= conversion_count;
+ str += conversion_count;
+
+ // Append the one or two UTF-16 characters
+ int out_count = out[1] ? 2 : 1;
+ size_t out_size = sizeof(uint16_t) * out_count;
+ result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
+ out_idx += out_count;
+ }
+ return result;
+}
+
+unsigned int MinidumpFileWriter::CalculateNumOfU16s(const wchar_t *str, unsigned int length) {
+ if (sizeof(wchar_t) == sizeof(uint16_t)) {
+ // Shortcut if wchar_t is the same size as MDString's buffer
+ return length;
+ } else {
+ uint16_t out[2];
+ unsigned int num_of_u16s = 0;
+
+ // Copy the string character by character
+ while (length) {
+ UTF32ToUTF16Char(*str, out);
+ if (!out[0])
+ return false;
+
+ // Process one character at a time
+ --length;
+ ++str;
+
+ // Count the number of UTF-16 characters needed for every wchar_t
+ // and add it to the total.
+ int out_count = out[1] ? 2 : 1;
+ num_of_u16s += out_count;
+ }
+
+ return num_of_u16s;
+ }
+}
+
+unsigned int MinidumpFileWriter::CalculateNumOfU16s(const char *str, unsigned int length) {
+ uint16_t out[2];
+ unsigned int num_of_u16s = 0;
+
+ while (length) {
+ int conversion_count = UTF8ToUTF16Char(str, length, out);
+ if (!conversion_count) {
+ break;
+ }
+ // Move the pointer along based on the number of converted characters
+ length -= conversion_count;
+ str += conversion_count;
+
+ // Add one or two to the total
+ int out_count = out[1] ? 2 : 1;
+ num_of_u16s += out_count;
+ }
+
+ return num_of_u16s;
+}
+
+
+template <typename CharType>
+bool MinidumpFileWriter::WriteStringCore(const CharType *str,
+ unsigned int length,
+ MDLocationDescriptor *location) {
+ assert(str);
+ assert(location);
+ // Calculate the mdstring length by either limiting to |length| as passed in
+ // or by finding the location of the NULL character.
+ unsigned int mdstring_length = 0;
+ if (!length)
+ length = INT_MAX;
+ for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
+ ;
+
+ unsigned int num_of_u16s = MinidumpFileWriter::CalculateNumOfU16s(str, mdstring_length);
+
+ // Allocate the string buffer
+ TypedMDRVA<MDString> mdstring(this);
+ if (!mdstring.AllocateObjectAndArray(num_of_u16s + 1, sizeof(uint16_t)))
+ return false;
+
+ // Set length excluding the NULL and copy the string
+ mdstring.get()->length =
+ static_cast<uint32_t>(num_of_u16s * sizeof(uint16_t));
+ bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
+
+ // NULL terminate
+ if (result) {
+ uint16_t ch = 0;
+ result = mdstring.CopyIndexAfterObject(num_of_u16s, &ch, sizeof(ch));
+
+ if (result)
+ *location = mdstring.location();
+ }
+
+ return result;
+}
+
+bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
+ MDLocationDescriptor *location) {
+ return WriteStringCore(str, length, location);
+}
+
+bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
+ MDLocationDescriptor *location) {
+ return WriteStringCore(str, length, location);
+}
+
+bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
+ MDMemoryDescriptor *output) {
+ assert(src);
+ assert(output);
+ UntypedMDRVA mem(this);
+
+ if (!mem.Allocate(size))
+ return false;
+ if (!mem.Copy(src, mem.size()))
+ return false;
+
+ output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
+ output->memory = mem.location();
+
+ return true;
+}
+
+MDRVA MinidumpFileWriter::Allocate(size_t size) {
+ assert(size);
+ assert(file_ != -1);
+#if defined(__ANDROID__)
+ if (NeedsFTruncateWorkAround()) {
+ // If ftruncate() is not available. We simply increase the size beyond the
+ // current file size. sys_write() will expand the file when data is written
+ // to it. Because we did not over allocate to fit memory pages, we also
+ // do not need to ftruncate() the file once we are done.
+ size_ += size;
+
+ // We don't need to seek since the file is unchanged.
+ MDRVA current_position = position_;
+ position_ += static_cast<MDRVA>(size);
+ return current_position;
+ }
+#endif
+ size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
+
+ if (position_ + aligned_size > size_) {
+ size_t growth = aligned_size;
+ size_t minimal_growth = getpagesize();
+
+ // Ensure that the file grows by at least the size of a memory page
+ if (growth < minimal_growth)
+ growth = minimal_growth;
+
+ size_t new_size = size_ + growth;
+ if (ftruncate(file_, new_size) != 0)
+ return kInvalidMDRVA;
+
+ size_ = new_size;
+ }
+
+ MDRVA current_position = position_;
+ position_ += static_cast<MDRVA>(aligned_size);
+
+ return current_position;
+}
+
+bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
+ assert(src);
+ assert(size);
+ assert(file_ != -1);
+
+ // Ensure that the data will fit in the allocated space
+ if (static_cast<size_t>(size + position) > size_)
+ return false;
+
+ // Seek and write the data
+#if defined(__linux__) && __linux__
+ if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
+ if (sys_write(file_, src, size) == size) {
+ return true;
+ }
+ }
+#else
+ if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
+ if (write(file_, src, size) == size) {
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+bool UntypedMDRVA::Allocate(size_t size) {
+ assert(size_ == 0);
+ size_ = size;
+ position_ = writer_->Allocate(size_);
+ return position_ != MinidumpFileWriter::kInvalidMDRVA;
+}
+
+bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
+ assert(src);
+ assert(size);
+ assert(pos + size <= position_ + size_);
+ return writer_->Copy(pos, src, size);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/minidump_file_writer.h b/toolkit/crashreporter/breakpad-client/minidump_file_writer.h
new file mode 100644
index 0000000000..4d41581027
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/minidump_file_writer.h
@@ -0,0 +1,281 @@
+// 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_file_writer.h: Implements file-based minidump generation. It's
+// intended to be used with the Google Breakpad open source crash handling
+// project.
+
+#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__
+#define CLIENT_MINIDUMP_FILE_WRITER_H__
+
+#include <string>
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+class UntypedMDRVA;
+template<typename MDType> class TypedMDRVA;
+
+// The user of this class can Open() a file and add minidump streams, data, and
+// strings using the definitions in minidump_format.h. Since this class is
+// expected to be used in a situation where the current process may be
+// damaged, it will not allocate heap memory.
+// Sample usage:
+// MinidumpFileWriter writer;
+// writer.Open("/tmp/minidump.dmp");
+// TypedMDRVA<MDRawHeader> header(&writer_);
+// header.Allocate();
+// header->get()->signature = MD_HEADER_SIGNATURE;
+// :
+// writer.Close();
+//
+// An alternative is to use SetFile and provide a file descriptor:
+// MinidumpFileWriter writer;
+// writer.SetFile(minidump_fd);
+// TypedMDRVA<MDRawHeader> header(&writer_);
+// header.Allocate();
+// header->get()->signature = MD_HEADER_SIGNATURE;
+// :
+// writer.Close();
+
+class MinidumpFileWriter {
+public:
+ // Invalid MDRVA (Minidump Relative Virtual Address)
+ // returned on failed allocation
+ static const MDRVA kInvalidMDRVA;
+
+ MinidumpFileWriter();
+ ~MinidumpFileWriter();
+
+ // Open |path| as the destination of the minidump data. If |path| already
+ // exists, then Open() will fail.
+ // Return true on success, or false on failure.
+ bool Open(const char *path);
+
+ // Sets the file descriptor |file| as the destination of the minidump data.
+ // Can be used as an alternative to Open() when a file descriptor is
+ // available.
+ // Note that |fd| is not closed when the instance of MinidumpFileWriter is
+ // destroyed.
+ void SetFile(const int file);
+
+ // Close the current file (that was either created when Open was called, or
+ // specified with SetFile).
+ // Return true on success, or false on failure.
+ bool Close();
+
+ // Copy the contents of |str| to a MDString and write it to the file.
+ // |str| is expected to be either UTF-16 or UTF-32 depending on the size
+ // of wchar_t.
+ // Maximum |length| of characters to copy from |str|, or specify 0 to use the
+ // entire NULL terminated string. Copying will stop at the first NULL.
+ // |location| the allocated location
+ // Return true on success, or false on failure
+ bool WriteString(const wchar_t *str, unsigned int length,
+ MDLocationDescriptor *location);
+
+ // Same as above, except with |str| as a UTF-8 string
+ bool WriteString(const char *str, unsigned int length,
+ MDLocationDescriptor *location);
+
+ // Write |size| bytes starting at |src| into the current position.
+ // Return true on success and set |output| to position, or false on failure
+ bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output);
+
+ // Copies |size| bytes from |src| to |position|
+ // Return true on success, or false on failure
+ bool Copy(MDRVA position, const void *src, ssize_t size);
+
+ // Return the current position for writing to the minidump
+ inline MDRVA position() const { return position_; }
+
+ private:
+ friend class UntypedMDRVA;
+
+ // Allocates an area of |size| bytes.
+ // Returns the position of the allocation, or kInvalidMDRVA if it was
+ // unable to allocate the bytes.
+ MDRVA Allocate(size_t size);
+
+ // The file descriptor for the output file.
+ int file_;
+
+ // Whether |file_| should be closed when the instance is destroyed.
+ bool close_file_when_destroyed_;
+
+ // Current position in buffer
+ MDRVA position_;
+
+ // Current allocated size
+ size_t size_;
+
+ // Copy |length| characters from |str| to |mdstring|. These are distinct
+ // because the underlying MDString is a UTF-16 based string. The wchar_t
+ // variant may need to create a MDString that has more characters than the
+ // source |str|, whereas the UTF-8 variant may coalesce characters to form
+ // a single UTF-16 character.
+ bool CopyStringToMDString(const wchar_t *str, unsigned int length,
+ TypedMDRVA<MDString> *mdstring);
+ bool CopyStringToMDString(const char *str, unsigned int length,
+ TypedMDRVA<MDString> *mdstring);
+
+ // The common templated code for writing a string
+ template <typename CharType>
+ bool WriteStringCore(const CharType *str, unsigned int length,
+ MDLocationDescriptor *location);
+
+
+ // Calculate the number of uint16_t values required to store a wchar_t string
+ // when its converted to UTF-16.
+ static unsigned int CalculateNumOfU16s(const wchar_t *str, unsigned int length);
+
+ // Calculate the number of uint16_t values required to store a UTF-8 string
+ // when its converted to UTF-16.
+ static unsigned int CalculateNumOfU16s(const char *str, unsigned int length);
+};
+
+// Represents an untyped allocated chunk
+class UntypedMDRVA {
+ public:
+ explicit UntypedMDRVA(MinidumpFileWriter *writer)
+ : writer_(writer),
+ position_(writer->position()),
+ size_(0) {}
+
+ // Allocates |size| bytes. Must not call more than once.
+ // Return true on success, or false on failure
+ bool Allocate(size_t size);
+
+ // Returns the current position or kInvalidMDRVA if allocation failed
+ inline MDRVA position() const { return position_; }
+
+ // Number of bytes allocated
+ inline size_t size() const { return size_; }
+
+ // Return size and position
+ inline MDLocationDescriptor location() const {
+ MDLocationDescriptor location = { static_cast<uint32_t>(size_),
+ position_ };
+ return location;
+ }
+
+ // Copy |size| bytes starting at |src| into the minidump at |position|
+ // Return true on success, or false on failure
+ bool Copy(MDRVA position, const void *src, size_t size);
+
+ // Copy |size| bytes from |src| to the current position
+ inline bool Copy(const void *src, size_t size) {
+ return Copy(position_, src, size);
+ }
+
+ protected:
+ // Writer we associate with
+ MinidumpFileWriter *writer_;
+
+ // Position of the start of the data
+ MDRVA position_;
+
+ // Allocated size
+ size_t size_;
+};
+
+// Represents a Minidump object chunk. Additional memory can be allocated at
+// the end of the object as a:
+// - single allocation
+// - Array of MDType objects
+// - A MDType object followed by an array
+template<typename MDType>
+class TypedMDRVA : public UntypedMDRVA {
+ public:
+ // Constructs an unallocated MDRVA
+ explicit TypedMDRVA(MinidumpFileWriter *writer)
+ : UntypedMDRVA(writer),
+ data_(),
+ allocation_state_(UNALLOCATED) {}
+
+ inline ~TypedMDRVA() {
+ // Ensure that the data_ object is written out
+ if (allocation_state_ != ARRAY)
+ Flush();
+ }
+
+ // Address of object data_ of MDType. This is not declared const as the
+ // typical usage will be to access the underlying |data_| object as to
+ // alter its contents.
+ MDType *get() { return &data_; }
+
+ // Allocates minidump_size<MDType>::size() bytes.
+ // Must not call more than once.
+ // Return true on success, or false on failure
+ bool Allocate();
+
+ // Allocates minidump_size<MDType>::size() + |additional| bytes.
+ // Must not call more than once.
+ // Return true on success, or false on failure
+ bool Allocate(size_t additional);
+
+ // Allocate an array of |count| elements of MDType.
+ // Must not call more than once.
+ // Return true on success, or false on failure
+ bool AllocateArray(size_t count);
+
+ // Allocate an array of |count| elements of |size| after object of MDType
+ // Must not call more than once.
+ // Return true on success, or false on failure
+ bool AllocateObjectAndArray(size_t count, size_t size);
+
+ // Copy |item| to |index|
+ // Must have been allocated using AllocateArray().
+ // Return true on success, or false on failure
+ bool CopyIndex(unsigned int index, MDType *item);
+
+ // Copy |size| bytes starting at |str| to |index|
+ // Must have been allocated using AllocateObjectAndArray().
+ // Return true on success, or false on failure
+ bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size);
+
+ // Write data_
+ bool Flush();
+
+ private:
+ enum AllocationState {
+ UNALLOCATED = 0,
+ SINGLE_OBJECT,
+ ARRAY,
+ SINGLE_OBJECT_WITH_ARRAY
+ };
+
+ MDType data_;
+ AllocationState allocation_state_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MINIDUMP_FILE_WRITER_H__
diff --git a/toolkit/crashreporter/breakpad-client/minidump_file_writer_unittest.cc b/toolkit/crashreporter/breakpad-client/minidump_file_writer_unittest.cc
new file mode 100644
index 0000000000..256e337127
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/minidump_file_writer_unittest.cc
@@ -0,0 +1,179 @@
+// 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.
+
+// Author: waylonis@google.com (Dan Waylonis)
+
+/*
+ g++ -I../ ../common/convert_UTF.cc \
+ ../common/string_conversion.cc \
+ minidump_file_writer.cc \
+ minidump_file_writer_unittest.cc \
+ -o minidump_file_writer_unittest
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "minidump_file_writer-inl.h"
+
+using google_breakpad::MinidumpFileWriter;
+
+#define ASSERT_TRUE(cond) \
+if (!(cond)) { \
+ fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
+ return false; \
+}
+
+#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
+#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2))
+
+struct StringStructure {
+ unsigned long integer_value;
+ MDLocationDescriptor first_string;
+ MDLocationDescriptor second_string;
+};
+
+struct ArrayStructure {
+ unsigned char char_value;
+ unsigned short short_value;
+ unsigned long long_value;
+};
+
+typedef struct {
+ unsigned long count;
+ ArrayStructure array[0];
+} ObjectAndArrayStructure;
+
+static bool WriteFile(const char *path) {
+ MinidumpFileWriter writer;
+ if (writer.Open(path)) {
+ // Test a single structure
+ google_breakpad::TypedMDRVA<StringStructure> strings(&writer);
+ ASSERT_TRUE(strings.Allocate());
+ strings.get()->integer_value = 0xBEEF;
+ const char *first = "First String";
+ ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string));
+ const wchar_t *second = L"Second String";
+ ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string));
+
+ // Test an array structure
+ google_breakpad::TypedMDRVA<ArrayStructure> array(&writer);
+ unsigned int count = 10;
+ ASSERT_TRUE(array.AllocateArray(count));
+ for (unsigned char i = 0; i < count; ++i) {
+ ArrayStructure local;
+ local.char_value = i;
+ local.short_value = i + 1;
+ local.long_value = i + 2;
+ ASSERT_TRUE(array.CopyIndex(i, &local));
+ }
+
+ // Test an object followed by an array
+ google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer);
+ ASSERT_TRUE(obj_array.AllocateObjectAndArray(count,
+ sizeof(ArrayStructure)));
+ obj_array.get()->count = count;
+ for (unsigned char i = 0; i < count; ++i) {
+ ArrayStructure local;
+ local.char_value = i;
+ local.short_value = i + 1;
+ local.long_value = i + 2;
+ ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local)));
+ }
+ }
+
+ return writer.Close();
+}
+
+static bool CompareFile(const char *path) {
+ unsigned long expected[] = {
+#if defined(__BIG_ENDIAN__)
+ 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
+ 0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069,
+ 0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064,
+ 0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002,
+ 0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005,
+ 0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008,
+ 0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b,
+ 0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003,
+ 0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006,
+ 0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
+ 0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
+#else
+ 0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
+ 0x00000038, 0x00000000, 0x00000018, 0x00690046,
+ 0x00730072, 0x00200074, 0x00740053, 0x00690072,
+ 0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
+ 0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
+ 0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
+ 0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
+ 0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
+ 0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
+ 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
+ 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
+ 0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
+ 0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
+ 0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
+ 0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
+ 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
+#endif
+ };
+ size_t expected_byte_count = sizeof(expected);
+ int fd = open(path, O_RDONLY, 0600);
+ void *buffer = malloc(expected_byte_count);
+ ASSERT_NE(fd, -1);
+ ASSERT_TRUE(buffer);
+ ASSERT_EQ(read(fd, buffer, expected_byte_count),
+ static_cast<ssize_t>(expected_byte_count));
+
+ char *b1, *b2;
+ b1 = reinterpret_cast<char*>(buffer);
+ b2 = reinterpret_cast<char*>(expected);
+ while (*b1 == *b2) {
+ b1++;
+ b2++;
+ }
+
+ printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer));
+
+ ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
+ return true;
+}
+
+static bool RunTests() {
+ const char *path = "/tmp/minidump_file_writer_unittest.dmp";
+ ASSERT_TRUE(WriteFile(path));
+ ASSERT_TRUE(CompareFile(path));
+ unlink(path);
+ return true;
+}
+
+extern "C" int main(int argc, const char *argv[]) {
+ return RunTests() ? 0 : 1;
+}
diff --git a/toolkit/crashreporter/breakpad-client/moz.build b/toolkit/crashreporter/breakpad-client/moz.build
new file mode 100644
index 0000000000..9ee42e4136
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/moz.build
@@ -0,0 +1,33 @@
+# -*- 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 += [
+ 'minidump_file_writer.cc',
+]
+
+Library('breakpad_client')
+
+USE_LIBS += [
+ 'breakpad_common_s',
+]
+
+if CONFIG['OS_ARCH'] == 'Darwin':
+ USE_LIBS += [
+ 'breakpad_mac_common_s',
+ ]
+elif CONFIG['OS_ARCH'] == 'Linux':
+ USE_LIBS += [
+ 'breakpad_linux_common_s',
+ ]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/toolkit/crashreporter/google-breakpad/src',
+]
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ CXXFLAGS += ['-Wno-error=stack-protector']
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h b/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h
new file mode 100644
index 0000000000..3fd4b9b7e6
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
+#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
+
+#include <windows.h>
+
+namespace google_breakpad {
+
+// Automatically enters the critical section in the constructor and leaves
+// the critical section in the destructor.
+class AutoCriticalSection {
+ public:
+ // Creates a new instance with the given critical section object
+ // and enters the critical section immediately.
+ explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs), taken_(false) {
+ assert(cs_);
+ Acquire();
+ }
+
+ // Destructor: leaves the critical section.
+ ~AutoCriticalSection() {
+ if (taken_) {
+ Release();
+ }
+ }
+
+ // Enters the critical section. Recursive Acquire() calls are not allowed.
+ void Acquire() {
+ assert(!taken_);
+ EnterCriticalSection(cs_);
+ taken_ = true;
+ }
+
+ // Leaves the critical section. The caller should not call Release() unless
+ // the critical seciton has been entered already.
+ void Release() {
+ assert(taken_);
+ taken_ = false;
+ LeaveCriticalSection(cs_);
+ }
+
+ private:
+ // Disable copy ctor and operator=.
+ AutoCriticalSection(const AutoCriticalSection&);
+ AutoCriticalSection& operator=(const AutoCriticalSection&);
+
+ CRITICAL_SECTION* cs_;
+ bool taken_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h b/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h
new file mode 100644
index 0000000000..c74868198c
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
+#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <string>
+#include <utility>
+#include "common/windows/string_utils-inl.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// Name/value pair for custom client information.
+struct CustomInfoEntry {
+ // Maximum length for name and value for client custom info.
+ static const int kNameMaxLength = 64;
+ static const int kValueMaxLength = 64;
+
+ CustomInfoEntry() {
+ // Putting name and value in initializer list makes VC++ show warning 4351.
+ set_name(NULL);
+ set_value(NULL);
+ }
+
+ CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) {
+ set_name(name_arg);
+ set_value(value_arg);
+ }
+
+ void set_name(const wchar_t* name_arg) {
+ if (!name_arg) {
+ name[0] = L'\0';
+ return;
+ }
+ WindowsStringUtils::safe_wcscpy(name, kNameMaxLength, name_arg);
+ }
+
+ void set_value(const wchar_t* value_arg) {
+ if (!value_arg) {
+ value[0] = L'\0';
+ return;
+ }
+
+ WindowsStringUtils::safe_wcscpy(value, kValueMaxLength, value_arg);
+ }
+
+ void set(const wchar_t* name_arg, const wchar_t* value_arg) {
+ set_name(name_arg);
+ set_value(value_arg);
+ }
+
+ wchar_t name[kNameMaxLength];
+ wchar_t value[kValueMaxLength];
+};
+
+// Constants for the protocol between client and the server.
+
+// Tags sent with each message indicating the purpose of
+// the message.
+enum MessageTag {
+ MESSAGE_TAG_NONE = 0,
+ MESSAGE_TAG_REGISTRATION_REQUEST = 1,
+ MESSAGE_TAG_REGISTRATION_RESPONSE = 2,
+ MESSAGE_TAG_REGISTRATION_ACK = 3,
+ MESSAGE_TAG_UPLOAD_REQUEST = 4
+};
+
+struct CustomClientInfo {
+ const CustomInfoEntry* entries;
+ size_t count;
+};
+
+// Message structure for IPC between crash client and crash server.
+struct ProtocolMessage {
+ ProtocolMessage()
+ : tag(MESSAGE_TAG_NONE),
+ id(0),
+ dump_type(MiniDumpNormal),
+ thread_id(0),
+ exception_pointers(NULL),
+ assert_info(NULL),
+ custom_client_info(),
+ dump_request_handle(NULL),
+ dump_generated_handle(NULL),
+ server_alive_handle(NULL) {
+ }
+
+ // Creates an instance with the given parameters.
+ ProtocolMessage(MessageTag arg_tag,
+ DWORD arg_id,
+ MINIDUMP_TYPE arg_dump_type,
+ DWORD* arg_thread_id,
+ EXCEPTION_POINTERS** arg_exception_pointers,
+ MDRawAssertionInfo* arg_assert_info,
+ const CustomClientInfo& custom_info,
+ HANDLE arg_dump_request_handle,
+ HANDLE arg_dump_generated_handle,
+ HANDLE arg_server_alive)
+ : tag(arg_tag),
+ id(arg_id),
+ dump_type(arg_dump_type),
+ thread_id(arg_thread_id),
+ exception_pointers(arg_exception_pointers),
+ assert_info(arg_assert_info),
+ custom_client_info(custom_info),
+ dump_request_handle(arg_dump_request_handle),
+ dump_generated_handle(arg_dump_generated_handle),
+ server_alive_handle(arg_server_alive) {
+ }
+
+ // Tag in the message.
+ MessageTag tag;
+
+ // The id for this message. This may be either a process id or a crash id
+ // depending on the type of message.
+ DWORD id;
+
+ // Dump type requested.
+ MINIDUMP_TYPE dump_type;
+
+ // Client thread id pointer.
+ DWORD* thread_id;
+
+ // Exception information.
+ EXCEPTION_POINTERS** exception_pointers;
+
+ // Assert information in case of an invalid parameter or
+ // pure call failure.
+ MDRawAssertionInfo* assert_info;
+
+ // Custom client information.
+ CustomClientInfo custom_client_info;
+
+ // Handle to signal the crash event.
+ HANDLE dump_request_handle;
+
+ // Handle to check if server is done generating crash.
+ HANDLE dump_generated_handle;
+
+ // Handle to a mutex that becomes signaled (WAIT_ABANDONED)
+ // if server process goes down.
+ HANDLE server_alive_handle;
+
+ private:
+ // Disable copy ctor and operator=.
+ ProtocolMessage(const ProtocolMessage& msg);
+ ProtocolMessage& operator=(const ProtocolMessage& msg);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc
new file mode 100644
index 0000000000..54d8e25274
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "minidump_callback.h"
+
+#include <winternl.h>
+
+#include <algorithm>
+#include <cassert>
+
+namespace google_breakpad {
+
+static const DWORD sHeapRegionSize= 1024;
+static DWORD sPageSize = 0;
+
+using NtQueryInformationThreadFunc = decltype(::NtQueryInformationThread);
+static NtQueryInformationThreadFunc* sNtQueryInformationThread = nullptr;
+
+
+namespace {
+enum {
+ ThreadBasicInformation,
+};
+
+struct CLIENT_ID {
+ PVOID UniqueProcess;
+ PVOID UniqueThread;
+};
+
+struct THREAD_BASIC_INFORMATION {
+ NTSTATUS ExitStatus;
+ PVOID TebBaseAddress;
+ CLIENT_ID ClientId;
+ KAFFINITY AffMask;
+ DWORD Priority;
+ DWORD BasePriority;
+};
+}
+
+void InitAppMemoryInternal()
+{
+ if (!sPageSize) {
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+ sPageSize = systemInfo.dwPageSize;
+ }
+
+ if (!sNtQueryInformationThread) {
+ sNtQueryInformationThread = (NtQueryInformationThreadFunc*)
+ (::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"),
+ "NtQueryInformationThread"));
+ }
+}
+
+bool GetAppMemoryFromRegister(HANDLE aProcess,
+ const NT_TIB* aTib,
+ RegisterValueType aRegister,
+ AppMemory* aResult)
+{
+ static_assert(sizeof(RegisterValueType) == sizeof(void*),
+ "Size mismatch between DWORD/DWORD64 and void*");
+
+ if (!sPageSize) {
+ // GetSystemInfo() should not fail, but bail out just in case it fails.
+ return false;
+ }
+
+ RegisterValueType addr = aRegister;
+ addr &= ~(static_cast<RegisterValueType>(sPageSize) - 1);
+
+ if (aTib) {
+ if (aRegister >= (RegisterValueType)aTib->StackLimit &&
+ aRegister <= (RegisterValueType)aTib->StackBase) {
+ // aRegister points to the stack.
+ return false;
+ }
+ }
+
+ MEMORY_BASIC_INFORMATION memInfo;
+ memset(&memInfo, 0, sizeof(memInfo));
+ SIZE_T rv = ::VirtualQueryEx(aProcess,
+ reinterpret_cast<void*>(addr),
+ &memInfo,
+ sizeof(memInfo));
+ if (!rv) {
+ // VirtualQuery fails: aAddr is not on heap.
+ return false;
+ }
+
+ // Check protection and type of the memory region. Include the region if it's
+ // 1. read-write: heap, or
+ // 2. read-executable and private: likely to be JIT code.
+ if (memInfo.Protect != PAGE_READWRITE &&
+ memInfo.Protect != PAGE_EXECUTE_READ) {
+ return false;
+ }
+
+ // Try to include a region of size sHeapRegionSize around aRegister, bounded
+ // by the [BaseAddress, BaseAddress + RegionSize].
+ RegisterValueType lower =
+ std::max(aRegister - sHeapRegionSize / 2,
+ reinterpret_cast<RegisterValueType>(memInfo.BaseAddress));
+
+ RegisterValueType upper =
+ std::min(lower + sHeapRegionSize,
+ reinterpret_cast<RegisterValueType>(memInfo.BaseAddress) +
+ memInfo.RegionSize);
+
+ aResult->ptr = lower;
+ aResult->length = upper - lower;
+
+ return true;
+}
+
+static AppMemoryList::iterator
+FindNextPreallocated(AppMemoryList& aList, AppMemoryList::iterator aBegin) {
+ auto it = aBegin;
+ for (auto it = aBegin; it != aList.end(); it++) {
+ if (it->preallocated) {
+ return it;
+ }
+ }
+
+ assert(it == aList.end());
+ return it;
+}
+
+static bool
+GetThreadTib(HANDLE aProcess, DWORD aThreadId, NT_TIB* aTib) {
+ HANDLE threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION,
+ FALSE,
+ aThreadId);
+ if (!threadHandle) {
+ return false;
+ }
+
+ if (!sNtQueryInformationThread) {
+ return false;
+ }
+
+ THREAD_BASIC_INFORMATION threadInfo;
+ auto status = (*sNtQueryInformationThread)(threadHandle,
+ (THREADINFOCLASS)ThreadBasicInformation,
+ &threadInfo,
+ sizeof(threadInfo),
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ return false;
+ }
+
+ auto readSuccess = ::ReadProcessMemory(aProcess,
+ threadInfo.TebBaseAddress,
+ aTib,
+ sizeof(*aTib),
+ NULL);
+ if (!readSuccess) {
+ return false;
+ }
+
+ ::CloseHandle(threadHandle);
+ return true;
+}
+
+void IncludeAppMemoryFromExceptionContext(HANDLE aProcess,
+ DWORD aThreadId,
+ AppMemoryList& aList,
+ PCONTEXT aExceptionContext,
+ bool aInstructionPointerOnly) {
+ RegisterValueType heapAddrCandidates[kExceptionAppMemoryRegions];
+ size_t numElements = 0;
+
+ NT_TIB tib;
+ memset(&tib, 0, sizeof(tib));
+ if (!GetThreadTib(aProcess, aThreadId, &tib)) {
+ // Fail to query thread stack range: only safe to include the region around
+ // the instruction pointer.
+ aInstructionPointerOnly = true;
+ }
+
+ // Add registers that might have a heap address to heapAddrCandidates.
+ // Note that older versions of DbgHelp.dll don't correctly put the memory
+ // around the faulting instruction pointer into the minidump. Include Rip/Eip
+ // unconditionally ensures it gets included.
+#if defined(_M_IX86)
+ if (!aInstructionPointerOnly) {
+ heapAddrCandidates[numElements++] = aExceptionContext->Eax;
+ heapAddrCandidates[numElements++] = aExceptionContext->Ebx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Ecx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Edx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Esi;
+ heapAddrCandidates[numElements++] = aExceptionContext->Edi;
+ }
+ heapAddrCandidates[numElements++] = aExceptionContext->Eip;
+#elif defined(_M_AMD64)
+ if (!aInstructionPointerOnly) {
+ heapAddrCandidates[numElements++] = aExceptionContext->Rax;
+ heapAddrCandidates[numElements++] = aExceptionContext->Rbx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Rcx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Rdx;
+ heapAddrCandidates[numElements++] = aExceptionContext->Rsi;
+ heapAddrCandidates[numElements++] = aExceptionContext->Rdi;
+ heapAddrCandidates[numElements++] = aExceptionContext->R8;
+ heapAddrCandidates[numElements++] = aExceptionContext->R9;
+ heapAddrCandidates[numElements++] = aExceptionContext->R10;
+ heapAddrCandidates[numElements++] = aExceptionContext->R11;
+ heapAddrCandidates[numElements++] = aExceptionContext->R12;
+ heapAddrCandidates[numElements++] = aExceptionContext->R13;
+ heapAddrCandidates[numElements++] = aExceptionContext->R14;
+ heapAddrCandidates[numElements++] = aExceptionContext->R15;
+ }
+ heapAddrCandidates[numElements++] = aExceptionContext->Rip;
+#elif defined(_M_ARM64)
+ if (!aInstructionPointerOnly) {
+ for (auto reg : aExceptionContext->X) {
+ heapAddrCandidates[numElements++] = reg;
+ }
+ heapAddrCandidates[numElements++] = aExceptionContext->Sp;
+ }
+ heapAddrCandidates[numElements++] = aExceptionContext->Pc;
+#endif
+
+ // Inplace sort the candidates for excluding or merging memory regions.
+ auto begin = &heapAddrCandidates[0], end = &heapAddrCandidates[numElements];
+ std::make_heap(begin, end);
+ std::sort_heap(begin, end);
+
+ auto appMemory = FindNextPreallocated(aList, aList.begin());
+ for (size_t i = 0; i < numElements; i++) {
+ if (appMemory == aList.end()) {
+ break;
+ }
+
+ AppMemory tmp{};
+ if (!GetAppMemoryFromRegister(aProcess,
+ aInstructionPointerOnly ? nullptr : &tib,
+ heapAddrCandidates[i],
+ &tmp)) {
+ continue;
+ }
+
+ if (!(tmp.ptr && tmp.length)) {
+ // Something unexpected happens. Skip this candidate.
+ continue;
+ }
+
+ if (!appMemory->ptr) {
+ *appMemory = tmp;
+ continue;
+ }
+
+ if (appMemory->ptr + appMemory->length > tmp.ptr) {
+ // The beginning of the next region fall within the range of the previous
+ // region: merge into one. Note that we don't merge adjacent regions like
+ // [0, 99] and [100, 199] in case we cross the border of memory allocation
+ // regions.
+ appMemory->length = tmp.ptr + tmp.length - appMemory->ptr;
+ continue;
+ }
+
+ appMemory = FindNextPreallocated(aList, ++appMemory);
+ if (appMemory == aList.end()) {
+ break;
+ }
+
+ *appMemory = tmp;
+ }
+}
+
+BOOL CALLBACK MinidumpWriteDumpCallback(
+ PVOID context,
+ const PMINIDUMP_CALLBACK_INPUT callback_input,
+ PMINIDUMP_CALLBACK_OUTPUT callback_output) {
+ switch (callback_input->CallbackType) {
+ case MemoryCallback: {
+ MinidumpCallbackContext* callback_context =
+ reinterpret_cast<MinidumpCallbackContext*>(context);
+
+ // Skip unused preallocated AppMemory elements.
+ while (callback_context->iter != callback_context->end &&
+ callback_context->iter->preallocated &&
+ !callback_context->iter->ptr) {
+ callback_context->iter++;
+ }
+
+ if (callback_context->iter == callback_context->end)
+ return FALSE;
+
+ // Include the specified memory region.
+ callback_output->MemoryBase = callback_context->iter->ptr;
+ callback_output->MemorySize = callback_context->iter->length;
+ callback_context->iter++;
+ return TRUE;
+ }
+
+ // Include all modules.
+ case IncludeModuleCallback:
+ case ModuleCallback:
+ return TRUE;
+
+ // Include all threads.
+ case IncludeThreadCallback:
+ case ThreadCallback:
+ return TRUE;
+
+ // Stop receiving cancel callbacks.
+ case CancelCallback:
+ callback_output->CheckCancel = FALSE;
+ callback_output->Cancel = FALSE;
+ return TRUE;
+ }
+ // Ignore other callback types.
+ return FALSE;
+}
+
+} // namespace google_breakpad
+
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h
new file mode 100644
index 0000000000..1306235cfd
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MINIDUMP_CALLBACK_H__
+#define MINIDUMP_CALLBACK_H__
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <list>
+
+namespace google_breakpad {
+
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+ ULONG64 ptr;
+ ULONG length;
+ bool preallocated;
+
+ bool operator==(const struct AppMemory& other) const {
+ return ptr == other.ptr;
+ }
+
+ bool operator==(const void* other) const {
+ return ptr == reinterpret_cast<ULONG64>(other);
+ }
+
+ AppMemory()
+ : ptr(0)
+ , length(0)
+ , preallocated(false)
+ {}
+
+ AppMemory& operator=(const AppMemory& other) = default;
+};
+typedef std::list<AppMemory> AppMemoryList;
+
+// This is passed as the context to the MinidumpWriteDump callback.
+typedef struct {
+ AppMemoryList::const_iterator iter;
+ AppMemoryList::const_iterator end;
+} MinidumpCallbackContext;
+
+static const size_t kExceptionAppMemoryRegions = 33;
+
+#if defined(_M_IX86)
+using RegisterValueType = DWORD;
+#elif defined(_M_AMD64) || defined(_M_ARM64)
+using RegisterValueType = DWORD64;
+#else
+#error Unsupported platform
+#endif
+
+void IncludeAppMemoryFromExceptionContext(HANDLE aProcess,
+ DWORD aThreadId,
+ AppMemoryList& aList,
+ PCONTEXT aExceptionContext,
+ bool aInsttructionPointerOnly);
+
+// This function is used as a callback when calling MinidumpWriteDump,
+// in order to add additional memory regions to the dump.
+BOOL CALLBACK MinidumpWriteDumpCallback(
+ PVOID context,
+ const PMINIDUMP_CALLBACK_INPUT callback_input,
+ PMINIDUMP_CALLBACK_OUTPUT callback_output);
+
+// Called during startup to initialize system information used by
+// IncludeAppMemoryFromExceptionContext().
+void InitAppMemoryInternal();
+
+} // namespace google_breakpad
+
+#endif
+
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild b/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild
new file mode 100644
index 0000000000..ddc1d58d56
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild
@@ -0,0 +1,14 @@
+# -*- 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/.
+
+lobjs_client_common = [
+ 'minidump_callback.cc',
+]
+
+subdir = 'toolkit/crashreporter/breakpad-client/windows/common'
+objs_client_common = [
+ '/%s/%s' % (subdir, s) for s in lobjs_client_common
+]
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/ReadMe.txt b/toolkit/crashreporter/breakpad-client/windows/crash_generation/ReadMe.txt
new file mode 100644
index 0000000000..b54d0e11b7
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/ReadMe.txt
@@ -0,0 +1,58 @@
+=========================================================================
+ State machine transitions for the Crash Generation Server
+=========================================================================
+
+=========================================================================
+ |
+ STATE | ACTIONS
+ |
+=========================================================================
+ ERROR | Clean up resources used to serve clients.
+ | Always remain in ERROR state.
+-------------------------------------------------------------------------
+ INITIAL | Connect to the pipe asynchronously.
+ | If connection is successfully queued up asynchronously,
+ | go into CONNECTING state.
+ | If connection is done synchronously, go into CONNECTED
+ | state.
+ | For any unexpected problems, go into ERROR state.
+-------------------------------------------------------------------------
+ CONNECTING | Get the result of async connection request.
+ | If I/O is still incomplete, remain in the CONNECTING
+ | state.
+ | If connection is complete, go into CONNECTED state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ CONNECTED | Read from the pipe asynchronously.
+ | If read request is successfully queued up asynchronously,
+ | go into READING state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ READING | Get the result of async read request.
+ | If read is done, go into READ_DONE state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ READ_DONE | Register the client, prepare the reply and write the
+ | reply to the pipe asynchronously.
+ | If write request is successfully queued up asynchronously,
+ | go into WRITING state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ WRITING | Get the result of the async write request.
+ | If write is done, go into WRITE_DONE state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ WRITE_DONE | Read from the pipe asynchronously (for an ACK).
+ | If read request is successfully queued up asynchonously,
+ | go into READING_ACK state.
+ | For any unexpected problems, go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ READING_ACK | Get the result of the async read request.
+ | If read is done, perform action for successful client
+ | connection.
+ | Go into DISCONNECTING state.
+-------------------------------------------------------------------------
+ DISCONNECTING | Disconnect from the pipe, reset the event and go into
+ | INITIAL state and signal the event again. If anything
+ | fails, go into ERROR state.
+=========================================================================
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.cc b/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.cc
new file mode 100644
index 0000000000..5e443241c1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2008, 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 <cassert>
+
+#include "windows/crash_generation/client_info.h"
+#include "windows/common/ipc_protocol.h"
+
+static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime";
+static const size_t kMaxCustomInfoEntries = 4096;
+
+namespace google_breakpad {
+
+ClientInfo::ClientInfo(CrashGenerationServer* crash_server,
+ DWORD pid,
+ MINIDUMP_TYPE dump_type,
+ DWORD* thread_id,
+ EXCEPTION_POINTERS** ex_info,
+ MDRawAssertionInfo* assert_info,
+ const CustomClientInfo& custom_client_info)
+ : crash_server_(crash_server),
+ pid_(pid),
+ dump_type_(dump_type),
+ ex_info_(ex_info),
+ assert_info_(assert_info),
+ custom_client_info_(custom_client_info),
+ thread_id_(thread_id),
+ process_handle_(NULL),
+ dump_requested_handle_(NULL),
+ dump_generated_handle_(NULL),
+ dump_request_wait_handle_(NULL),
+ process_exit_wait_handle_(NULL),
+ crash_id_(NULL) {
+ GetSystemTimeAsFileTime(&start_time_);
+}
+
+bool ClientInfo::Initialize() {
+ process_handle_ = OpenProcess(GENERIC_ALL, FALSE, pid_);
+ if (!process_handle_) {
+ return false;
+ }
+
+ // The crash_id will be the low order word of the process creation time.
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ if (GetProcessTimes(process_handle_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ start_time_ = creation_time;
+ }
+ crash_id_ = start_time_.dwLowDateTime;
+
+ dump_requested_handle_ = CreateEvent(NULL, // Security attributes.
+ TRUE, // Manual reset.
+ FALSE, // Initial state.
+ NULL); // Name.
+ if (!dump_requested_handle_) {
+ return false;
+ }
+
+ dump_generated_handle_ = CreateEvent(NULL, // Security attributes.
+ TRUE, // Manual reset.
+ FALSE, // Initial state.
+ NULL); // Name.
+ return dump_generated_handle_ != NULL;
+}
+
+void ClientInfo::UnregisterDumpRequestWaitAndBlockUntilNoPending() {
+ if (dump_request_wait_handle_) {
+ // Wait for callbacks that might already be running to finish.
+ UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE);
+ dump_request_wait_handle_ = NULL;
+ }
+}
+
+void ClientInfo::UnregisterProcessExitWait(bool block_until_no_pending) {
+ if (process_exit_wait_handle_) {
+ if (block_until_no_pending) {
+ // Wait for the callback that might already be running to finish.
+ UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE);
+ } else {
+ UnregisterWait(process_exit_wait_handle_);
+ }
+ process_exit_wait_handle_ = NULL;
+ }
+}
+
+ClientInfo::~ClientInfo() {
+ // Waiting for the callback to finish here is safe because ClientInfo's are
+ // never destroyed from the dump request handling callback.
+ UnregisterDumpRequestWaitAndBlockUntilNoPending();
+
+ // This is a little tricky because ClientInfo's may be destroyed by the same
+ // callback (OnClientEnd) and waiting for it to finish will cause a deadlock.
+ // Regardless of this complication, wait for any running callbacks to finish
+ // so that the common case is properly handled. In order to avoid deadlocks,
+ // the OnClientEnd callback must call UnregisterProcessExitWait(false)
+ // before deleting the ClientInfo.
+ UnregisterProcessExitWait(true);
+
+ if (process_handle_) {
+ CloseHandle(process_handle_);
+ }
+
+ if (dump_requested_handle_) {
+ CloseHandle(dump_requested_handle_);
+ }
+
+ if (dump_generated_handle_) {
+ CloseHandle(dump_generated_handle_);
+ }
+}
+
+bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info_ptr) const {
+ SIZE_T bytes_count = 0;
+
+ static_assert(sizeof(*ex_info_ptr) == sizeof(void*),
+ "Expected to read sizeof(void*) bytes.");
+ if (!ReadProcessMemory(process_handle_,
+ ex_info_,
+ ex_info_ptr,
+ sizeof(*ex_info_ptr),
+ &bytes_count)) {
+ return false;
+ }
+
+ return bytes_count == sizeof(*ex_info_ptr);
+}
+
+bool ClientInfo::PopulateClientExceptionContext(EXCEPTION_POINTERS* ex_info_ptr,
+ CONTEXT* out_context) const {
+ SIZE_T bytes_count = 0;
+
+ EXCEPTION_POINTERS ex_info;
+ if (!ReadProcessMemory(process_handle_,
+ ex_info_ptr,
+ &ex_info,
+ sizeof(ex_info),
+ &bytes_count)) {
+ return false;
+ }
+
+ if (bytes_count != sizeof(ex_info)) {
+ return false;
+ }
+
+ static_assert(sizeof(*out_context) == sizeof(CONTEXT),
+ "Expected to read sizeof(CONTEXT) bytes.");
+ if (!ReadProcessMemory(process_handle_,
+ ex_info.ContextRecord,
+ out_context,
+ sizeof(*out_context),
+ &bytes_count)) {
+ return false;
+ }
+
+ return bytes_count == sizeof(*out_context);
+}
+
+bool ClientInfo::GetClientThreadId(DWORD* thread_id) const {
+ SIZE_T bytes_count = 0;
+ if (!ReadProcessMemory(process_handle_,
+ thread_id_,
+ thread_id,
+ sizeof(*thread_id),
+ &bytes_count)) {
+ return false;
+ }
+
+ return bytes_count == sizeof(*thread_id);
+}
+
+void ClientInfo::SetProcessUptime() {
+ FILETIME now = {0};
+ GetSystemTimeAsFileTime(&now);
+
+ ULARGE_INTEGER time_start;
+ time_start.HighPart = start_time_.dwHighDateTime;
+ time_start.LowPart = start_time_.dwLowDateTime;
+
+ ULARGE_INTEGER time_now;
+ time_now.HighPart = now.dwHighDateTime;
+ time_now.LowPart = now.dwLowDateTime;
+
+ // Calculate the delay and convert it from 100-nanoseconds to milliseconds.
+ __int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000;
+
+ // Convert it to a string.
+ wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value;
+ _i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10);
+}
+
+bool ClientInfo::PopulateCustomInfo() {
+ if (custom_client_info_.count > kMaxCustomInfoEntries)
+ return false;
+
+ SIZE_T bytes_count = 0;
+ SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count;
+
+ // If the scoped array for custom info already has an array, it will be
+ // the same size as what we need. This is because the number of custom info
+ // entries is always the same. So allocate memory only if scoped array has
+ // a NULL pointer.
+ if (!custom_info_entries_.get()) {
+ // Allocate an extra entry for reporting uptime for the client process.
+ custom_info_entries_.reset(
+ new CustomInfoEntry[custom_client_info_.count + 1]);
+ // Use the last element in the array for uptime.
+ custom_info_entries_.get()[custom_client_info_.count].set_name(
+ kCustomInfoProcessUptimeName);
+ }
+
+ if (!ReadProcessMemory(process_handle_,
+ custom_client_info_.entries,
+ custom_info_entries_.get(),
+ read_count,
+ &bytes_count)) {
+ return false;
+ }
+
+ SetProcessUptime();
+ return (bytes_count == read_count);
+}
+
+CustomClientInfo ClientInfo::GetCustomInfo() const {
+ CustomClientInfo custom_info;
+ custom_info.entries = custom_info_entries_.get();
+ // Add 1 to the count from the client process to account for extra entry for
+ // process uptime.
+ custom_info.count = custom_client_info_.count + 1;
+ return custom_info;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.h b/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.h
new file mode 100644
index 0000000000..506099dcdc
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/client_info.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
+#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
+
+#include <windows.h>
+#include <dbghelp.h>
+#include "windows/common/ipc_protocol.h"
+#include "common/scoped_ptr.h"
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+class CrashGenerationServer;
+
+// Abstraction for a crash client process.
+class ClientInfo {
+ public:
+ // Creates an instance with the given values. Gets the process
+ // handle for the given process id and creates necessary event
+ // objects.
+ ClientInfo(CrashGenerationServer* crash_server,
+ DWORD pid,
+ MINIDUMP_TYPE dump_type,
+ DWORD* thread_id,
+ EXCEPTION_POINTERS** ex_info,
+ MDRawAssertionInfo* assert_info,
+ const CustomClientInfo& custom_client_info);
+
+ ~ClientInfo();
+
+ CrashGenerationServer* crash_server() const { return crash_server_; }
+ DWORD pid() const { return pid_; }
+ MINIDUMP_TYPE dump_type() const { return dump_type_; }
+ EXCEPTION_POINTERS** ex_info() const { return ex_info_; }
+ MDRawAssertionInfo* assert_info() const { return assert_info_; }
+ DWORD* thread_id() const { return thread_id_; }
+ HANDLE process_handle() const { return process_handle_; }
+ HANDLE dump_requested_handle() const { return dump_requested_handle_; }
+ HANDLE dump_generated_handle() const { return dump_generated_handle_; }
+ DWORD crash_id() const { return crash_id_; }
+ const CustomClientInfo& custom_client_info() const {
+ return custom_client_info_;
+ }
+
+ void set_dump_request_wait_handle(HANDLE value) {
+ dump_request_wait_handle_ = value;
+ }
+
+ void set_process_exit_wait_handle(HANDLE value) {
+ process_exit_wait_handle_ = value;
+ }
+
+ // Unregister the dump request wait operation and wait for all callbacks
+ // that might already be running to complete before returning.
+ void UnregisterDumpRequestWaitAndBlockUntilNoPending();
+
+ // Unregister the process exit wait operation. If block_until_no_pending is
+ // true, wait for all callbacks that might already be running to complete
+ // before returning.
+ void UnregisterProcessExitWait(bool block_until_no_pending);
+
+ bool Initialize();
+ bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info_ptr) const;
+
+ // Reads the content of exception CONTEXT from the client process.
+ bool PopulateClientExceptionContext(EXCEPTION_POINTERS* ex_info,
+ CONTEXT* out_context) const;
+
+ bool GetClientThreadId(DWORD* thread_id) const;
+
+ // Reads the custom information from the client process address space.
+ bool PopulateCustomInfo();
+
+ // Returns the client custom information.
+ CustomClientInfo GetCustomInfo() const;
+
+ private:
+ // Calcualtes the uptime for the client process, converts it to a string and
+ // stores it in the last entry of client custom info.
+ void SetProcessUptime();
+
+ // Crash generation server.
+ CrashGenerationServer* crash_server_;
+
+ // Client process ID.
+ DWORD pid_;
+
+ // Dump type requested by the client.
+ MINIDUMP_TYPE dump_type_;
+
+ // Address of an EXCEPTION_POINTERS* variable in the client
+ // process address space that will point to an instance of
+ // EXCEPTION_POINTERS containing information about crash.
+ //
+ // WARNING: Do not dereference these pointers as they are pointers
+ // in the address space of another process.
+ EXCEPTION_POINTERS** ex_info_;
+
+ // Address of an instance of MDRawAssertionInfo in the client
+ // process address space that will contain information about
+ // non-exception related crashes like invalid parameter assertion
+ // failures and pure calls.
+ //
+ // WARNING: Do not dereference these pointers as they are pointers
+ // in the address space of another process.
+ MDRawAssertionInfo* assert_info_;
+
+ // Custom information about the client.
+ CustomClientInfo custom_client_info_;
+
+ // Contains the custom client info entries read from the client process
+ // memory. This will be populated only if the method GetClientCustomInfo
+ // is called.
+ scoped_array<CustomInfoEntry> custom_info_entries_;
+
+ // Address of a variable in the client process address space that
+ // will contain the thread id of the crashing client thread.
+ //
+ // WARNING: Do not dereference these pointers as they are pointers
+ // in the address space of another process.
+ DWORD* thread_id_;
+
+ // Client process handle.
+ HANDLE process_handle_;
+
+ // Dump request event handle.
+ HANDLE dump_requested_handle_;
+
+ // Dump generated event handle.
+ HANDLE dump_generated_handle_;
+
+ // Wait handle for dump request event.
+ HANDLE dump_request_wait_handle_;
+
+ // Wait handle for process exit event.
+ HANDLE process_exit_wait_handle_;
+
+ // Time when the client process started. It is used to determine the uptime
+ // for the client process when it signals a crash.
+ FILETIME start_time_;
+
+ // The crash id which can be used to request an upload. This will be the
+ // value of the low order dword of the process creation time for the process
+ // being dumped.
+ DWORD crash_id_;
+
+ // Disallow copy ctor and operator=.
+ ClientInfo(const ClientInfo& client_info);
+ ClientInfo& operator=(const ClientInfo& client_info);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.cc b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.cc
new file mode 100644
index 0000000000..0e524e7854
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.cc
@@ -0,0 +1,406 @@
+// Copyright (c) 2008, 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 "windows/crash_generation/crash_generation_client.h"
+#include <cassert>
+#include <utility>
+#include "windows/common/ipc_protocol.h"
+
+namespace google_breakpad {
+
+const int kPipeBusyWaitTimeoutMs = 2000;
+
+#ifdef _DEBUG
+const DWORD kWaitForServerTimeoutMs = INFINITE;
+#else
+const DWORD kWaitForServerTimeoutMs = 15000;
+#endif
+
+const int kPipeConnectMaxAttempts = 6;
+
+const DWORD kPipeDesiredAccess = FILE_READ_DATA |
+ FILE_WRITE_DATA |
+ FILE_WRITE_ATTRIBUTES;
+
+const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
+ SECURITY_SQOS_PRESENT;
+
+const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
+
+const size_t kWaitEventCount = 2;
+
+// This function is orphan for production code. It can be used
+// for debugging to help repro some scenarios like the client
+// is slow in writing to the pipe after connecting, the client
+// is slow in reading from the pipe after writing, etc. The parameter
+// overlapped below is not used and it is present to match the signature
+// of this function to TransactNamedPipe Win32 API. Uncomment if needed
+// for debugging.
+/**
+static bool TransactNamedPipeDebugHelper(HANDLE pipe,
+ const void* in_buffer,
+ DWORD in_size,
+ void* out_buffer,
+ DWORD out_size,
+ DWORD* bytes_count,
+ LPOVERLAPPED) {
+ // Uncomment the next sleep to create a gap before writing
+ // to pipe.
+ // Sleep(5000);
+
+ if (!WriteFile(pipe,
+ in_buffer,
+ in_size,
+ bytes_count,
+ NULL)) {
+ return false;
+ }
+
+ // Uncomment the next sleep to create a gap between write
+ // and read.
+ // Sleep(5000);
+
+ return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE;
+}
+**/
+
+CrashGenerationClient::CrashGenerationClient(
+ const wchar_t* pipe_name,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info)
+ : pipe_name_(pipe_name),
+ pipe_handle_(NULL),
+ custom_info_(),
+ dump_type_(dump_type),
+ crash_event_(NULL),
+ crash_generated_(NULL),
+ server_alive_(NULL),
+ server_process_id_(0),
+ thread_id_(0),
+ exception_pointers_(NULL) {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ if (custom_info) {
+ custom_info_ = *custom_info;
+ }
+}
+
+CrashGenerationClient::CrashGenerationClient(
+ HANDLE pipe_handle,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info)
+ : pipe_name_(),
+ pipe_handle_(pipe_handle),
+ custom_info_(),
+ dump_type_(dump_type),
+ crash_event_(NULL),
+ crash_generated_(NULL),
+ server_alive_(NULL),
+ server_process_id_(0),
+ thread_id_(0),
+ exception_pointers_(NULL) {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ if (custom_info) {
+ custom_info_ = *custom_info;
+ }
+}
+
+CrashGenerationClient::~CrashGenerationClient() {
+ if (crash_event_) {
+ CloseHandle(crash_event_);
+ }
+
+ if (crash_generated_) {
+ CloseHandle(crash_generated_);
+ }
+
+ if (server_alive_) {
+ CloseHandle(server_alive_);
+ }
+}
+
+// Performs the registration step with the server process.
+// The registration step involves communicating with the server
+// via a named pipe. The client sends the following pieces of
+// data to the server:
+//
+// * Message tag indicating the client is requesting registration.
+// * Process id of the client process.
+// * Address of a DWORD variable in the client address space
+// that will contain the thread id of the client thread that
+// caused the crash.
+// * Address of a EXCEPTION_POINTERS* variable in the client
+// address space that will point to an instance of EXCEPTION_POINTERS
+// when the crash happens.
+// * Address of an instance of MDRawAssertionInfo that will contain
+// relevant information in case of non-exception crashes like assertion
+// failures and pure calls.
+//
+// In return the client expects the following information from the server:
+//
+// * Message tag indicating successful registration.
+// * Server process id.
+// * Handle to an object that client can signal to request dump
+// generation from the server.
+// * Handle to an object that client can wait on after requesting
+// dump generation for the server to finish dump generation.
+// * Handle to a mutex object that client can wait on to make sure
+// server is still alive.
+//
+// If any step of the expected behavior mentioned above fails, the
+// registration step is not considered successful and hence out-of-process
+// dump generation service is not available.
+//
+// Returns true if the registration is successful; false otherwise.
+bool CrashGenerationClient::Register() {
+ if (IsRegistered()) {
+ return true;
+ }
+
+ HANDLE pipe = ConnectToServer();
+ if (!pipe) {
+ return false;
+ }
+
+ bool success = RegisterClient(pipe);
+ CloseHandle(pipe);
+ return success;
+}
+
+bool CrashGenerationClient::RequestUpload(DWORD crash_id) {
+ HANDLE pipe = ConnectToServer();
+ if (!pipe) {
+ return false;
+ }
+
+ CustomClientInfo custom_info = {NULL, 0};
+ ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id,
+ static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL,
+ custom_info, NULL, NULL, NULL);
+ DWORD bytes_count = 0;
+ bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0;
+
+ CloseHandle(pipe);
+ return success;
+}
+
+HANDLE CrashGenerationClient::ConnectToServer() {
+ HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
+ kPipeDesiredAccess,
+ kPipeFlagsAndAttributes);
+ if (!pipe) {
+ return NULL;
+ }
+
+ DWORD mode = kPipeMode;
+ if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
+ CloseHandle(pipe);
+ pipe = NULL;
+ }
+
+ return pipe;
+}
+
+bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
+ ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
+ GetCurrentProcessId(),
+ dump_type_,
+ &thread_id_,
+ &exception_pointers_,
+ &assert_info_,
+ custom_info_,
+ NULL,
+ NULL,
+ NULL);
+ ProtocolMessage reply;
+ DWORD bytes_count = 0;
+ // The call to TransactNamedPipe below can be changed to a call
+ // to TransactNamedPipeDebugHelper to help repro some scenarios.
+ // For details see comments for TransactNamedPipeDebugHelper.
+ if (!TransactNamedPipe(pipe,
+ &msg,
+ sizeof(msg),
+ &reply,
+ sizeof(ProtocolMessage),
+ &bytes_count,
+ NULL)) {
+ return false;
+ }
+
+ if (!ValidateResponse(reply)) {
+ return false;
+ }
+
+ ProtocolMessage ack_msg;
+ ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
+
+ if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
+ return false;
+ }
+ crash_event_ = reply.dump_request_handle;
+ crash_generated_ = reply.dump_generated_handle;
+ server_alive_ = reply.server_alive_handle;
+ server_process_id_ = reply.id;
+
+ return true;
+}
+
+HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
+ DWORD pipe_access,
+ DWORD flags_attrs) {
+ if (pipe_handle_) {
+ HANDLE t = pipe_handle_;
+ pipe_handle_ = NULL;
+ return t;
+ }
+
+ for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
+ HANDLE pipe = CreateFile(pipe_name,
+ pipe_access,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ flags_attrs,
+ NULL);
+ if (pipe != INVALID_HANDLE_VALUE) {
+ return pipe;
+ }
+
+ // Cannot continue retrying if error is something other than
+ // ERROR_PIPE_BUSY.
+ if (GetLastError() != ERROR_PIPE_BUSY) {
+ break;
+ }
+
+ // Cannot continue retrying if wait on pipe fails.
+ if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
+ break;
+ }
+ }
+
+ fprintf(stderr, "ConnectToPipe() failure: GetLastError()=%08lx\n", GetLastError());
+ return NULL;
+}
+
+bool CrashGenerationClient::ValidateResponse(
+ const ProtocolMessage& msg) const {
+ return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
+ (msg.id != 0) &&
+ (msg.dump_request_handle != NULL) &&
+ (msg.dump_generated_handle != NULL) &&
+ (msg.server_alive_handle != NULL);
+}
+
+bool CrashGenerationClient::IsRegistered() const {
+ return crash_event_ != NULL;
+}
+
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
+ MDRawAssertionInfo* assert_info) {
+ if (!IsRegistered()) {
+ return false;
+ }
+
+ exception_pointers_ = ex_info;
+ thread_id_ = GetCurrentThreadId();
+
+ if (assert_info) {
+ memcpy(&assert_info_, assert_info, sizeof(assert_info_));
+ } else {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ }
+
+ return SignalCrashEventAndWait();
+}
+
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
+ return RequestDump(ex_info, NULL);
+}
+
+bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
+ return RequestDump(NULL, assert_info);
+}
+
+bool CrashGenerationClient::SignalCrashEventAndWait() {
+ assert(crash_event_);
+ assert(crash_generated_);
+ assert(server_alive_);
+
+ // Reset the dump generated event before signaling the crash
+ // event so that the server can set the dump generated event
+ // once it is done generating the event.
+ if (!ResetEvent(crash_generated_)) {
+ return false;
+ }
+
+ if (!SetEvent(crash_event_)) {
+ return false;
+ }
+
+ HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
+
+ DWORD result = WaitForMultipleObjects(kWaitEventCount,
+ wait_handles,
+ FALSE,
+ kWaitForServerTimeoutMs);
+
+ // Crash dump was successfully generated only if the server
+ // signaled the crash generated event.
+ return result == WAIT_OBJECT_0;
+}
+
+HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name,
+ HANDLE hProcess) {
+ for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
+ HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess,
+ 0, NULL, OPEN_EXISTING,
+ kPipeFlagsAndAttributes, NULL);
+ if (local_pipe != INVALID_HANDLE_VALUE) {
+ HANDLE remotePipe = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(GetCurrentProcess(), local_pipe,
+ hProcess, &remotePipe, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return remotePipe;
+ } else {
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+
+ // Cannot continue retrying if the error wasn't a busy pipe.
+ if (GetLastError() != ERROR_PIPE_BUSY) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.h b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.h
new file mode 100644
index 0000000000..0bfcdb3731
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_client.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <string>
+#include <utility>
+#include "windows/common/ipc_protocol.h"
+#include "common/scoped_ptr.h"
+
+namespace google_breakpad {
+
+struct CustomClientInfo;
+
+// Abstraction of client-side implementation of out of process
+// crash generation.
+//
+// The process that desires to have out-of-process crash dump
+// generation service can use this class in the following way:
+//
+// * Create an instance.
+// * Call Register method so that the client tries to register
+// with the server process and check the return value. If
+// registration is not successful, out-of-process crash dump
+// generation will not be available
+// * Request dump generation by calling either of the two
+// overloaded RequestDump methods - one in case of exceptions
+// and the other in case of assertion failures
+//
+// Note that it is the responsibility of the client code of
+// this class to set the unhandled exception filter with the
+// system by calling the SetUnhandledExceptionFilter function
+// and the client code should explicitly request dump generation.
+class CrashGenerationClient {
+ public:
+ CrashGenerationClient(const wchar_t* pipe_name,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info);
+
+ CrashGenerationClient(HANDLE pipe_handle,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info);
+
+ ~CrashGenerationClient();
+
+ // Registers the client process with the crash server.
+ //
+ // Returns true if the registration is successful; false otherwise.
+ bool Register();
+
+ // Requests the crash server to upload a previous dump with the
+ // given crash id.
+ bool RequestUpload(DWORD crash_id);
+
+ bool RequestDump(EXCEPTION_POINTERS* ex_info,
+ MDRawAssertionInfo* assert_info);
+
+ // Requests the crash server to generate a dump with the given
+ // exception information.
+ //
+ // Returns true if the dump was successful; false otherwise. Note that
+ // if the registration step was not performed or it was not successful,
+ // false will be returned.
+ bool RequestDump(EXCEPTION_POINTERS* ex_info);
+
+ // Requests the crash server to generate a dump with the given
+ // assertion information.
+ //
+ // Returns true if the dump was successful; false otherwise. Note that
+ // if the registration step was not performed or it was not successful,
+ // false will be returned.
+ bool RequestDump(MDRawAssertionInfo* assert_info);
+
+ // If the crash generation client is running in a sandbox that prevents it
+ // from opening the named pipe directly, the server process may open the
+ // handle and duplicate it into the client process with this helper method.
+ // Returns INVALID_HANDLE_VALUE on failure. The process must have been opened
+ // with the PROCESS_DUP_HANDLE access right.
+ static HANDLE DuplicatePipeToClientProcess(const wchar_t* pipe_name,
+ HANDLE hProcess);
+
+ private:
+ // Connects to the appropriate pipe and sets the pipe handle state.
+ //
+ // Returns the pipe handle if everything goes well; otherwise Returns NULL.
+ HANDLE ConnectToServer();
+
+ // Performs a handshake with the server over the given pipe which should be
+ // already connected to the server.
+ //
+ // Returns true if handshake with the server was successful; false otherwise.
+ bool RegisterClient(HANDLE pipe);
+
+ // Validates the given server response.
+ bool ValidateResponse(const ProtocolMessage& msg) const;
+
+ // Returns true if the registration step succeeded; false otherwise.
+ bool IsRegistered() const;
+
+ // Connects to the given named pipe with given parameters.
+ //
+ // Returns true if the connection is successful; false otherwise.
+ HANDLE ConnectToPipe(const wchar_t* pipe_name,
+ DWORD pipe_access,
+ DWORD flags_attrs);
+
+ // Signals the crash event and wait for the server to generate crash.
+ bool SignalCrashEventAndWait();
+
+ // Pipe name to use to talk to server.
+ std::wstring pipe_name_;
+
+ // Pipe handle duplicated from server process. Only valid before
+ // Register is called.
+ HANDLE pipe_handle_;
+
+ // Custom client information
+ CustomClientInfo custom_info_;
+
+ // Type of dump to generate.
+ MINIDUMP_TYPE dump_type_;
+
+ // Event to signal in case of a crash.
+ HANDLE crash_event_;
+
+ // Handle to wait on after signaling a crash for the server
+ // to finish generating crash dump.
+ HANDLE crash_generated_;
+
+ // Handle to a mutex that will become signaled with WAIT_ABANDONED
+ // if the server process goes down.
+ HANDLE server_alive_;
+
+ // Server process id.
+ DWORD server_process_id_;
+
+ // Id of the thread that caused the crash.
+ DWORD thread_id_;
+
+ // Exception pointers for an exception crash.
+ EXCEPTION_POINTERS* exception_pointers_;
+
+ // Assertion info for an invalid parameter or pure call crash.
+ MDRawAssertionInfo assert_info_;
+
+ // Disable copy ctor and operator=.
+ CrashGenerationClient(const CrashGenerationClient& crash_client);
+ CrashGenerationClient& operator=(const CrashGenerationClient& crash_client);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.cc
new file mode 100644
index 0000000000..dfa6d5161f
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.cc
@@ -0,0 +1,996 @@
+// Copyright (c) 2008, 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 "windows/crash_generation/crash_generation_server.h"
+#include <windows.h>
+#include <cassert>
+#include <list>
+#include "windows/common/auto_critical_section.h"
+#include "common/scoped_ptr.h"
+
+#include "windows/crash_generation/client_info.h"
+
+namespace google_breakpad {
+
+// Output buffer size.
+static const size_t kOutBufferSize = 64;
+
+// Input buffer size.
+static const size_t kInBufferSize = 64;
+
+// Access flags for the client on the dump request event.
+static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE;
+
+// Access flags for the client on the dump generated event.
+static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE |
+ SYNCHRONIZE;
+
+// Access flags for the client on the mutex.
+static const DWORD kMutexAccess = SYNCHRONIZE;
+
+// Attribute flags for the pipe.
+static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE |
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_OVERLAPPED;
+
+// Mode for the pipe.
+static const DWORD kPipeMode = PIPE_TYPE_MESSAGE |
+ PIPE_READMODE_MESSAGE |
+ PIPE_WAIT;
+
+// For pipe I/O, execute the callback in the wait thread itself,
+// since the callback does very little work. The callback executes
+// the code for one of the states of the server state machine and
+// the code for all of the states perform async I/O and hence
+// finish very quickly.
+static const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD;
+
+// Dump request threads will, most likely, generate dumps. That may
+// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag.
+static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD |
+ WT_EXECUTELONGFUNCTION;
+
+static bool IsClientRequestValid(const ProtocolMessage& msg) {
+ return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST ||
+ (msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST &&
+ msg.id != 0 &&
+ msg.thread_id != NULL &&
+ msg.exception_pointers != NULL &&
+ msg.assert_info != NULL);
+}
+
+#ifndef NDEBUG
+static bool CheckForIOIncomplete(bool success) {
+ // We should never get an I/O incomplete since we should not execute this
+ // unless the operation has finished and the overlapped event is signaled. If
+ // we do get INCOMPLETE, we have a bug in our code.
+ return success ? false : (GetLastError() == ERROR_IO_INCOMPLETE);
+}
+#endif
+
+CrashGenerationServer::CrashGenerationServer(
+ const std::wstring& pipe_name,
+ SECURITY_ATTRIBUTES* pipe_sec_attrs,
+ OnClientConnectedCallback connect_callback,
+ void* connect_context,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientDumpWrittenCallback written_callback,
+ OnClientExitedCallback exit_callback,
+ void* exit_context,
+ OnClientUploadRequestCallback upload_request_callback,
+ void* upload_context,
+ bool generate_dumps,
+ const std::wstring* dump_path)
+ : pipe_name_(pipe_name),
+ pipe_sec_attrs_(pipe_sec_attrs),
+ pipe_(NULL),
+ pipe_wait_handle_(NULL),
+ server_alive_handle_(NULL),
+ connect_callback_(connect_callback),
+ connect_context_(connect_context),
+ dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ written_callback_(written_callback),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ upload_request_callback_(upload_request_callback),
+ upload_context_(upload_context),
+ generate_dumps_(generate_dumps),
+ pre_fetch_custom_info_(true),
+ dump_path_(dump_path ? *dump_path : L""),
+ server_state_(IPC_SERVER_STATE_UNINITIALIZED),
+ shutting_down_(false),
+ overlapped_(),
+ client_info_(NULL),
+ include_context_heap_(false) {
+ InitializeCriticalSection(&sync_);
+}
+
+// This should never be called from the OnPipeConnected callback.
+// Otherwise the UnregisterWaitEx call below will cause a deadlock.
+CrashGenerationServer::~CrashGenerationServer() {
+ // New scope to release the lock automatically.
+ {
+ // Make sure no clients are added or removed beyond this point.
+ // Before adding or removing any clients, the critical section
+ // must be entered and the shutting_down_ flag checked. The
+ // critical section is then exited only after the clients_ list
+ // modifications are done and the list is in a consistent state.
+ AutoCriticalSection lock(&sync_);
+
+ // Indicate to existing threads that server is shutting down.
+ shutting_down_ = true;
+ }
+ // No one will modify the clients_ list beyond this point -
+ // not even from another thread.
+
+ // Even if there are no current worker threads running, it is possible that
+ // an I/O request is pending on the pipe right now but not yet done.
+ // In fact, it's very likely this is the case unless we are in an ERROR
+ // state. If we don't wait for the pending I/O to be done, then when the I/O
+ // completes, it may write to invalid memory. AppVerifier will flag this
+ // problem too. So we disconnect from the pipe and then wait for the server
+ // to get into error state so that the pending I/O will fail and get
+ // cleared.
+ DisconnectNamedPipe(pipe_);
+ int num_tries = 100;
+ while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) {
+ Sleep(10);
+ }
+
+ // Unregister wait on the pipe.
+ if (pipe_wait_handle_) {
+ // Wait for already executing callbacks to finish.
+ UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE);
+ }
+
+ // Close the pipe to avoid further client connections.
+ if (pipe_) {
+ CloseHandle(pipe_);
+ }
+
+ // Request all ClientInfo objects to unregister all waits.
+ // No need to enter the critical section because no one is allowed to modify
+ // the clients_ list once the shutting_down_ flag is set.
+ std::list<ClientInfo*>::iterator iter;
+ for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
+ ClientInfo* client_info = *iter;
+ // Unregister waits. Wait for already executing callbacks to finish.
+ // Unregister the client process exit wait first and only then unregister
+ // the dump request wait. The reason is that the OnClientExit callback
+ // also unregisters the dump request wait and such a race (doing the same
+ // unregistration from two threads) is undesirable.
+ client_info->UnregisterProcessExitWait(true);
+ client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending();
+
+ // Destroying the ClientInfo here is safe because all wait operations for
+ // this ClientInfo were unregistered and no pending or running callbacks
+ // for this ClientInfo can possible exist (block_until_no_pending option
+ // was used).
+ delete client_info;
+ }
+
+ if (server_alive_handle_) {
+ // Release the mutex before closing the handle so that clients requesting
+ // dumps wait for a long time for the server to generate a dump.
+ ReleaseMutex(server_alive_handle_);
+ CloseHandle(server_alive_handle_);
+ }
+
+ if (overlapped_.hEvent) {
+ CloseHandle(overlapped_.hEvent);
+ }
+
+ DeleteCriticalSection(&sync_);
+}
+
+bool CrashGenerationServer::Start() {
+ if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) {
+ return false;
+ }
+
+ server_state_ = IPC_SERVER_STATE_INITIAL;
+
+ server_alive_handle_ = CreateMutex(NULL, TRUE, NULL);
+ if (!server_alive_handle_) {
+ return false;
+ }
+
+ // Event to signal the client connection and pipe reads and writes.
+ overlapped_.hEvent = CreateEvent(NULL, // Security descriptor.
+ TRUE, // Manual reset.
+ FALSE, // Initially nonsignaled.
+ NULL); // Name.
+ if (!overlapped_.hEvent) {
+ return false;
+ }
+
+ // Register a callback with the thread pool for the client connection.
+ if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
+ overlapped_.hEvent,
+ OnPipeConnected,
+ this,
+ INFINITE,
+ kPipeIOThreadFlags)) {
+ return false;
+ }
+
+ pipe_ = CreateNamedPipe(pipe_name_.c_str(),
+ kPipeAttr,
+ kPipeMode,
+ 1,
+ kOutBufferSize,
+ kInBufferSize,
+ 0,
+ pipe_sec_attrs_);
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ // Kick-start the state machine. This will initiate an asynchronous wait
+ // for client connections.
+ if (!SetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ return false;
+ }
+
+ // If we are in error state, it's because we failed to start listening.
+ return true;
+}
+
+// If the server thread serving clients ever gets into the
+// ERROR state, reset the event, close the pipe and remain
+// in the error state forever. Error state means something
+// that we didn't account for has happened, and it's dangerous
+// to do anything unknowingly.
+void CrashGenerationServer::HandleErrorState() {
+ assert(server_state_ == IPC_SERVER_STATE_ERROR);
+
+ // If the server is shutting down anyway, don't clean up
+ // here since shut down process will clean up.
+ if (shutting_down_) {
+ return;
+ }
+
+ if (pipe_wait_handle_) {
+ UnregisterWait(pipe_wait_handle_);
+ pipe_wait_handle_ = NULL;
+ }
+
+ if (pipe_) {
+ CloseHandle(pipe_);
+ pipe_ = NULL;
+ }
+
+ if (overlapped_.hEvent) {
+ CloseHandle(overlapped_.hEvent);
+ overlapped_.hEvent = NULL;
+ }
+}
+
+// When the server thread serving clients is in the INITIAL state,
+// try to connect to the pipe asynchronously. If the connection
+// finishes synchronously, directly go into the CONNECTED state;
+// otherwise go into the CONNECTING state. For any problems, go
+// into the ERROR state.
+void CrashGenerationServer::HandleInitialState() {
+ assert(server_state_ == IPC_SERVER_STATE_INITIAL);
+
+ if (!ResetEvent(overlapped_.hEvent)) {
+ EnterErrorState();
+ return;
+ }
+
+ bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
+ DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+ // From MSDN, it is not clear that when ConnectNamedPipe is used
+ // in an overlapped mode, will it ever return non-zero value, and
+ // if so, in what cases.
+ assert(!success);
+
+ switch (error_code) {
+ case ERROR_IO_PENDING:
+ EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING);
+ break;
+
+ case ERROR_PIPE_CONNECTED:
+ EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
+ break;
+
+ default:
+ EnterErrorState();
+ break;
+ }
+}
+
+// When the server thread serving the clients is in the CONNECTING state,
+// try to get the result of the asynchronous connection request using
+// the OVERLAPPED object. If the result indicates the connection is done,
+// go into the CONNECTED state. If the result indicates I/O is still
+// INCOMPLETE, remain in the CONNECTING state. For any problems,
+// go into the DISCONNECTING state.
+void CrashGenerationServer::HandleConnectingState() {
+ assert(server_state_ == IPC_SERVER_STATE_CONNECTING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+ DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+ if (success) {
+ EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
+ } else if (error_code != ERROR_IO_INCOMPLETE) {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ } else {
+ // remain in CONNECTING state
+ }
+}
+
+// When the server thread serving the clients is in the CONNECTED state,
+// try to issue an asynchronous read from the pipe. If read completes
+// synchronously or if I/O is pending then go into the READING state.
+// For any problems, go into the DISCONNECTING state.
+void CrashGenerationServer::HandleConnectedState() {
+ assert(server_state_ == IPC_SERVER_STATE_CONNECTED);
+
+ DWORD bytes_count = 0;
+ memset(&msg_, 0, sizeof(msg_));
+ bool success = ReadFile(pipe_,
+ &msg_,
+ sizeof(msg_),
+ &bytes_count,
+ &overlapped_) != FALSE;
+ DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+ // Note that the asynchronous read issued above can finish before the
+ // code below executes. But, it is okay to change state after issuing
+ // the asynchronous read. This is because even if the asynchronous read
+ // is done, the callback for it would not be executed until the current
+ // thread finishes its execution.
+ if (success || error_code == ERROR_IO_PENDING) {
+ EnterStateWhenSignaled(IPC_SERVER_STATE_READING);
+ } else {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ }
+}
+
+// When the server thread serving the clients is in the READING state,
+// try to get the result of the async read. If async read is done,
+// go into the READ_DONE state. For any problems, go into the
+// DISCONNECTING state.
+void CrashGenerationServer::HandleReadingState() {
+ assert(server_state_ == IPC_SERVER_STATE_READING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+ if (success && bytes_count == sizeof(ProtocolMessage)) {
+ EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
+ return;
+ }
+
+ assert(!CheckForIOIncomplete(success));
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+}
+
+// When the server thread serving the client is in the READ_DONE state,
+// validate the client's request message, register the client by
+// creating appropriate objects and prepare the response. Then try to
+// write the response to the pipe asynchronously. If that succeeds,
+// go into the WRITING state. For any problems, go into the DISCONNECTING
+// state.
+void CrashGenerationServer::HandleReadDoneState() {
+ assert(server_state_ == IPC_SERVER_STATE_READ_DONE);
+
+ if (!IsClientRequestValid(msg_)) {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ return;
+ }
+
+ if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) {
+ if (upload_request_callback_)
+ upload_request_callback_(upload_context_, msg_.id);
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ return;
+ }
+
+ scoped_ptr<ClientInfo> client_info(
+ new ClientInfo(this,
+ msg_.id,
+ msg_.dump_type,
+ msg_.thread_id,
+ msg_.exception_pointers,
+ msg_.assert_info,
+ msg_.custom_client_info));
+
+ if (!client_info->Initialize()) {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ return;
+ }
+
+ // Issues an asynchronous WriteFile call if successful.
+ // Iff successful, assigns ownership of the client_info pointer to the server
+ // instance, in which case we must be sure not to free it in this function.
+ if (!RespondToClient(client_info.get())) {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ return;
+ }
+
+ // This is only valid as long as it can be found in the clients_ list
+ client_info_ = client_info.release();
+
+ // Note that the asynchronous write issued by RespondToClient function
+ // can finish before the code below executes. But it is okay to change
+ // state after issuing the asynchronous write. This is because even if
+ // the asynchronous write is done, the callback for it would not be
+ // executed until the current thread finishes its execution.
+ EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING);
+}
+
+// When the server thread serving the clients is in the WRITING state,
+// try to get the result of the async write. If the async write is done,
+// go into the WRITE_DONE state. For any problems, go into the
+// DISONNECTING state.
+void CrashGenerationServer::HandleWritingState() {
+ assert(server_state_ == IPC_SERVER_STATE_WRITING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+ if (success) {
+ EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
+ return;
+ }
+
+ assert(!CheckForIOIncomplete(success));
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+}
+
+// When the server thread serving the clients is in the WRITE_DONE state,
+// try to issue an async read on the pipe. If the read completes synchronously
+// or if I/O is still pending then go into the READING_ACK state. For any
+// issues, go into the DISCONNECTING state.
+void CrashGenerationServer::HandleWriteDoneState() {
+ assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE);
+
+ DWORD bytes_count = 0;
+ bool success = ReadFile(pipe_,
+ &msg_,
+ sizeof(msg_),
+ &bytes_count,
+ &overlapped_) != FALSE;
+ DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+ if (success) {
+ EnterStateImmediately(IPC_SERVER_STATE_READING_ACK);
+ } else if (error_code == ERROR_IO_PENDING) {
+ EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK);
+ } else {
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+ }
+}
+
+// When the server thread serving the clients is in the READING_ACK state,
+// try to get result of async read. Go into the DISCONNECTING state.
+void CrashGenerationServer::HandleReadingAckState() {
+ assert(server_state_ == IPC_SERVER_STATE_READING_ACK);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+ if (success) {
+ // The connection handshake with the client is now complete; perform
+ // the callback.
+ if (connect_callback_) {
+ // Note that there is only a single copy of the ClientInfo of the
+ // currently connected client. However it is being referenced from
+ // two different places:
+ // - the client_info_ member
+ // - the clients_ list
+ // The lifetime of this ClientInfo depends on the lifetime of the
+ // client process - basically it can go away at any time.
+ // However, as long as it is referenced by the clients_ list it
+ // is guaranteed to be valid. Enter the critical section and check
+ // to see whether the client_info_ can be found in the list.
+ // If found, execute the callback and only then leave the critical
+ // section.
+ AutoCriticalSection lock(&sync_);
+
+ bool client_is_still_alive = false;
+ std::list<ClientInfo*>::iterator iter;
+ for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
+ if (client_info_ == *iter) {
+ client_is_still_alive = true;
+ break;
+ }
+ }
+
+ if (client_is_still_alive) {
+ connect_callback_(connect_context_, client_info_);
+ }
+ }
+ } else {
+ assert(!CheckForIOIncomplete(success));
+ }
+
+ EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+}
+
+// When the server thread serving the client is in the DISCONNECTING state,
+// disconnect from the pipe and reset the event. If anything fails, go into
+// the ERROR state. If it goes well, go into the INITIAL state and set the
+// event to start all over again.
+void CrashGenerationServer::HandleDisconnectingState() {
+ assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING);
+
+ // Done serving the client.
+ client_info_ = NULL;
+
+ overlapped_.Internal = NULL;
+ overlapped_.InternalHigh = NULL;
+ overlapped_.Offset = 0;
+ overlapped_.OffsetHigh = 0;
+ overlapped_.Pointer = NULL;
+
+ if (!ResetEvent(overlapped_.hEvent)) {
+ EnterErrorState();
+ return;
+ }
+
+ if (!DisconnectNamedPipe(pipe_)) {
+ EnterErrorState();
+ return;
+ }
+
+ // If the server is shutting down do not connect to the
+ // next client.
+ if (shutting_down_) {
+ return;
+ }
+
+ EnterStateImmediately(IPC_SERVER_STATE_INITIAL);
+}
+
+void CrashGenerationServer::EnterErrorState() {
+ SetEvent(overlapped_.hEvent);
+ server_state_ = IPC_SERVER_STATE_ERROR;
+}
+
+void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) {
+ server_state_ = state;
+}
+
+void CrashGenerationServer::EnterStateImmediately(IPCServerState state) {
+ server_state_ = state;
+
+ if (!SetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ }
+}
+
+bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info,
+ ProtocolMessage* reply) const {
+ reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE;
+ reply->id = GetCurrentProcessId();
+
+ if (CreateClientHandles(client_info, reply)) {
+ return true;
+ }
+
+ // Closing of remote handles (belonging to a different process) can
+ // only be done through DuplicateHandle.
+ if (reply->dump_request_handle) {
+ DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
+ reply->dump_request_handle, // hSourceHandle
+ NULL, // hTargetProcessHandle
+ 0, // lpTargetHandle
+ 0, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ DUPLICATE_CLOSE_SOURCE); // dwOptions
+ reply->dump_request_handle = NULL;
+ }
+
+ if (reply->dump_generated_handle) {
+ DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
+ reply->dump_generated_handle, // hSourceHandle
+ NULL, // hTargetProcessHandle
+ 0, // lpTargetHandle
+ 0, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ DUPLICATE_CLOSE_SOURCE); // dwOptions
+ reply->dump_generated_handle = NULL;
+ }
+
+ if (reply->server_alive_handle) {
+ DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
+ reply->server_alive_handle, // hSourceHandle
+ NULL, // hTargetProcessHandle
+ 0, // lpTargetHandle
+ 0, // dwDesiredAccess
+ FALSE, // bInheritHandle
+ DUPLICATE_CLOSE_SOURCE); // dwOptions
+ reply->server_alive_handle = NULL;
+ }
+
+ return false;
+}
+
+bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info,
+ ProtocolMessage* reply) const {
+ HANDLE current_process = GetCurrentProcess();
+ if (!DuplicateHandle(current_process,
+ client_info.dump_requested_handle(),
+ client_info.process_handle(),
+ &reply->dump_request_handle,
+ kDumpRequestEventAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ if (!DuplicateHandle(current_process,
+ client_info.dump_generated_handle(),
+ client_info.process_handle(),
+ &reply->dump_generated_handle,
+ kDumpGeneratedEventAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ if (!DuplicateHandle(current_process,
+ server_alive_handle_,
+ client_info.process_handle(),
+ &reply->server_alive_handle,
+ kMutexAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) {
+ ProtocolMessage reply;
+ if (!PrepareReply(*client_info, &reply)) {
+ return false;
+ }
+
+ DWORD bytes_count = 0;
+ bool success = WriteFile(pipe_,
+ &reply,
+ sizeof(reply),
+ &bytes_count,
+ &overlapped_) != FALSE;
+ DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+ if (!success && error_code != ERROR_IO_PENDING) {
+ return false;
+ }
+
+ // Takes over ownership of client_info. We MUST return true if AddClient
+ // succeeds.
+ return AddClient(client_info);
+}
+
+// The server thread servicing the clients runs this method. The method
+// implements the state machine described in ReadMe.txt along with the
+// helper methods HandleXXXState.
+void CrashGenerationServer::HandleConnectionRequest() {
+ // If the server is shutting down, get into ERROR state, reset the event so
+ // more workers don't run and return immediately.
+ if (shutting_down_) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ ResetEvent(overlapped_.hEvent);
+ return;
+ }
+
+ switch (server_state_) {
+ case IPC_SERVER_STATE_ERROR:
+ HandleErrorState();
+ break;
+
+ case IPC_SERVER_STATE_INITIAL:
+ HandleInitialState();
+ break;
+
+ case IPC_SERVER_STATE_CONNECTING:
+ HandleConnectingState();
+ break;
+
+ case IPC_SERVER_STATE_CONNECTED:
+ HandleConnectedState();
+ break;
+
+ case IPC_SERVER_STATE_READING:
+ HandleReadingState();
+ break;
+
+ case IPC_SERVER_STATE_READ_DONE:
+ HandleReadDoneState();
+ break;
+
+ case IPC_SERVER_STATE_WRITING:
+ HandleWritingState();
+ break;
+
+ case IPC_SERVER_STATE_WRITE_DONE:
+ HandleWriteDoneState();
+ break;
+
+ case IPC_SERVER_STATE_READING_ACK:
+ HandleReadingAckState();
+ break;
+
+ case IPC_SERVER_STATE_DISCONNECTING:
+ HandleDisconnectingState();
+ break;
+
+ default:
+ assert(false);
+ // This indicates that we added one more state without
+ // adding handling code.
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ break;
+ }
+}
+
+bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
+ HANDLE request_wait_handle = NULL;
+ if (!RegisterWaitForSingleObject(&request_wait_handle,
+ client_info->dump_requested_handle(),
+ OnDumpRequest,
+ client_info,
+ INFINITE,
+ kDumpRequestThreadFlags)) {
+ return false;
+ }
+
+ client_info->set_dump_request_wait_handle(request_wait_handle);
+
+ // New scope to hold the lock for the shortest time.
+ {
+ AutoCriticalSection lock(&sync_);
+
+ // OnClientEnd will be called when the client process terminates.
+ HANDLE process_wait_handle = NULL;
+ if (!RegisterWaitForSingleObject(&process_wait_handle,
+ client_info->process_handle(),
+ OnClientEnd,
+ client_info,
+ INFINITE,
+ WT_EXECUTEONLYONCE)) {
+ return false;
+ }
+
+ client_info->set_process_exit_wait_handle(process_wait_handle);
+
+ if (shutting_down_) {
+ // If server is shutting down, don't add new clients
+ return false;
+ }
+ clients_.push_back(client_info);
+ }
+
+ return true;
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
+ assert(context);
+
+ CrashGenerationServer* obj =
+ reinterpret_cast<CrashGenerationServer*>(context);
+ obj->HandleConnectionRequest();
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
+ assert(context);
+ ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
+
+ CrashGenerationServer* crash_server = client_info->crash_server();
+ assert(crash_server);
+ if (crash_server->pre_fetch_custom_info_) {
+ client_info->PopulateCustomInfo();
+ }
+ crash_server->HandleDumpRequest(*client_info);
+
+ ResetEvent(client_info->dump_requested_handle());
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) {
+ assert(context);
+ ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
+
+ CrashGenerationServer* crash_server = client_info->crash_server();
+ assert(crash_server);
+
+ crash_server->HandleClientProcessExit(client_info);
+}
+
+void CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) {
+ assert(client_info);
+
+ // Must unregister the dump request wait operation and wait for any
+ // dump requests that might be pending to finish before proceeding
+ // with the client_info cleanup.
+ client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending();
+
+ if (exit_callback_) {
+ exit_callback_(exit_context_, *client_info);
+ }
+
+ // Start a new scope to release lock automatically.
+ {
+ AutoCriticalSection lock(&sync_);
+ if (shutting_down_) {
+ // The crash generation server is shutting down and as part of the
+ // shutdown process it will delete all clients from the clients_ list.
+ return;
+ }
+ clients_.remove(client_info);
+ }
+
+ AutoCriticalSection lock(&sync_);
+
+ // Explicitly unregister the process exit wait using the non-blocking method.
+ // Otherwise, the destructor will attempt to unregister it using the blocking
+ // method which will lead to a deadlock because it is being called from the
+ // callback of the same wait operation
+ client_info->UnregisterProcessExitWait(false);
+
+ delete client_info;
+}
+
+void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) {
+ bool execute_callback = true;
+ // Generate the dump only if it's explicitly requested by the
+ // server application; otherwise the server might want to generate
+ // dump in the callback.
+ std::wstring dump_path;
+ if (generate_dumps_) {
+ if (!GenerateDump(client_info, &dump_path)) {
+ // client proccess terminated or some other error
+ execute_callback = false;
+ }
+ }
+
+ if (dump_callback_ && execute_callback) {
+ dump_callback_(dump_context_, client_info, dump_path);
+ }
+
+ SetEvent(client_info.dump_generated_handle());
+
+ if (written_callback_ && execute_callback) {
+ written_callback_(dump_context_, client_info);
+ }
+}
+
+void CrashGenerationServer::set_include_context_heap(bool enabled) {
+ include_context_heap_ = enabled;
+}
+
+bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
+ std::wstring* dump_path) {
+ assert(client.pid() != 0);
+ assert(client.process_handle());
+
+ DWORD client_thread_id = 0;
+ if (!client.GetClientThreadId(&client_thread_id)) {
+ return false;
+ }
+
+ // We have to get the address of EXCEPTION_INFORMATION from
+ // the client process address space.
+ EXCEPTION_POINTERS* client_ex_info = NULL;
+
+ // Only needs to read the value of a remote pointer in client_ex_info.
+ if (!client.GetClientExceptionInfo(&client_ex_info)) {
+ return false;
+ }
+
+ if (include_context_heap_) {
+ CONTEXT context_content;
+
+ // Needs to read the content of CONTEXT from the client.
+ if (!client.PopulateClientExceptionContext(client_ex_info,
+ &context_content)) {
+ include_context_heap_ = false;
+ }
+
+ // Allocate AppMemory instances for exception context.
+ for (size_t i = 0; i < kExceptionAppMemoryRegions; i++) {
+ AppMemory app_memory;
+ app_memory.ptr = reinterpret_cast<ULONG64>(nullptr);
+ app_memory.length = 0;
+ app_memory.preallocated = true;
+ app_memory_info_.push_back(app_memory);
+ }
+
+ IncludeAppMemoryFromExceptionContext(client.process_handle(),
+ client_thread_id,
+ app_memory_info_,
+ &context_content,
+ !include_context_heap_);
+ }
+
+ MinidumpGenerator dump_generator(dump_path_,
+ client.process_handle(),
+ client.pid(),
+ client_thread_id,
+ GetCurrentThreadId(),
+ client_ex_info,
+ client.assert_info(),
+ client.dump_type(),
+ true);
+
+ MinidumpCallbackContext callback_context;
+ MINIDUMP_CALLBACK_INFORMATION callback;
+ if (include_context_heap_) {
+ // Set memory callback to include heap regions.
+ callback_context.iter = app_memory_info_.cbegin();
+ callback_context.end = app_memory_info_.cend();
+
+ callback.CallbackRoutine = MinidumpWriteDumpCallback;
+ callback.CallbackParam = &callback_context;
+
+ dump_generator.SetCallback(&callback);
+ }
+
+ if (!dump_generator.GenerateDumpFile(dump_path)) {
+ return false;
+ }
+
+ // If the client requests a full memory dump, we will write a normal mini
+ // dump and a full memory dump. Both dump files use the same uuid as file
+ // name prefix.
+ if (client.dump_type() & MiniDumpWithFullMemory) {
+ std::wstring full_dump_path;
+ if (!dump_generator.GenerateFullDumpFile(&full_dump_path)) {
+ return false;
+ }
+ }
+
+ return dump_generator.WriteMinidump();
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.h b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.h
new file mode 100644
index 0000000000..cc1912cf3c
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.h
@@ -0,0 +1,318 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
+#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
+
+#include <list>
+#include <string>
+#include "windows/common/minidump_callback.h"
+#include "windows/common/ipc_protocol.h"
+#include "windows/crash_generation/minidump_generator.h"
+#include "common/scoped_ptr.h"
+
+namespace google_breakpad {
+class ClientInfo;
+
+// Abstraction for server side implementation of out-of-process crash
+// generation protocol for Windows platform only. It generates Windows
+// minidump files for client processes that request dump generation. When
+// the server is requested to start listening for clients (by calling the
+// Start method), it creates a named pipe and waits for the clients to
+// register. In response, it hands them event handles that the client can
+// signal to request dump generation. When the clients request dump
+// generation in this way, the server generates Windows minidump files.
+class CrashGenerationServer {
+ public:
+ typedef void (*OnClientConnectedCallback)(void* context,
+ const ClientInfo* client_info);
+
+ typedef void (*OnClientDumpRequestCallback)(void* context,
+ const ClientInfo& client_info,
+ const std::wstring& file_path);
+
+ typedef void (*OnClientDumpWrittenCallback)(void* context,
+ const ClientInfo& client_info);
+
+ typedef void (*OnClientExitedCallback)(void* context,
+ const ClientInfo& client_info);
+
+ typedef void (*OnClientUploadRequestCallback)(void* context,
+ const DWORD crash_id);
+
+ // Creates an instance with the given parameters.
+ //
+ // Parameter pipe_name: Name of the Windows named pipe
+ // Parameter pipe_sec_attrs Security attributes to set on the pipe. Pass
+ // NULL to use default security on the pipe. By default, the pipe created
+ // allows Local System, Administrators and the Creator full control and
+ // the Everyone group read access on the pipe.
+ // Parameter connect_callback: Callback for a new client connection.
+ // Parameter connect_context: Context for client connection callback.
+ // Parameter dump_callback: Callback for a client crash dump request.
+ // Parameter dump_context: Context for client crash dump request callback.
+ // Parameter written_callback: Callback called after a crash dump was written.
+ // Parameter exit_callback: Callback for client process exit.
+ // Parameter exit_context: Context for client exit callback.
+ // Parameter generate_dumps: Whether to automatically generate dumps.
+ // Client code of this class might want to generate dumps explicitly in the
+ // crash dump request callback. In that case, false can be passed for this
+ // parameter.
+ // Parameter dump_path: Path for generating dumps; required only if true is
+ // passed for generateDumps parameter; NULL can be passed otherwise.
+ CrashGenerationServer(const std::wstring& pipe_name,
+ SECURITY_ATTRIBUTES* pipe_sec_attrs,
+ OnClientConnectedCallback connect_callback,
+ void* connect_context,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientDumpWrittenCallback written_callback,
+ OnClientExitedCallback exit_callback,
+ void* exit_context,
+ OnClientUploadRequestCallback upload_request_callback,
+ void* upload_context,
+ bool generate_dumps,
+ const std::wstring* dump_path);
+
+ ~CrashGenerationServer();
+
+ // Performs initialization steps needed to start listening to clients. Upon
+ // successful return clients may connect to this server's pipe.
+ //
+ // Returns true if initialization is successful; false otherwise.
+ bool Start();
+
+ void pre_fetch_custom_info(bool do_pre_fetch) {
+ pre_fetch_custom_info_ = do_pre_fetch;
+ }
+
+ // Calling set_include_context_heap(true) causes heap regions to be included
+ // in the minidump when a crash happens. The heap regions are from the
+ // register values of the client crashing context.
+ void set_include_context_heap(bool enabled);
+
+ private:
+ // Various states the client can be in during the handshake with
+ // the server.
+ enum IPCServerState {
+ // Server starts in this state.
+ IPC_SERVER_STATE_UNINITIALIZED,
+
+ // Server is in error state and it cannot serve any clients.
+ IPC_SERVER_STATE_ERROR,
+
+ // Server starts in this state.
+ IPC_SERVER_STATE_INITIAL,
+
+ // Server has issued an async connect to the pipe and it is waiting
+ // for the connection to be established.
+ IPC_SERVER_STATE_CONNECTING,
+
+ // Server is connected successfully.
+ IPC_SERVER_STATE_CONNECTED,
+
+ // Server has issued an async read from the pipe and it is waiting for
+ // the read to finish.
+ IPC_SERVER_STATE_READING,
+
+ // Server is done reading from the pipe.
+ IPC_SERVER_STATE_READ_DONE,
+
+ // Server has issued an async write to the pipe and it is waiting for
+ // the write to finish.
+ IPC_SERVER_STATE_WRITING,
+
+ // Server is done writing to the pipe.
+ IPC_SERVER_STATE_WRITE_DONE,
+
+ // Server has issued an async read from the pipe for an ack and it
+ // is waiting for the read to finish.
+ IPC_SERVER_STATE_READING_ACK,
+
+ // Server is done writing to the pipe and it is now ready to disconnect
+ // and reconnect.
+ IPC_SERVER_STATE_DISCONNECTING
+ };
+
+ //
+ // Helper methods to handle various server IPC states.
+ //
+ void HandleErrorState();
+ void HandleInitialState();
+ void HandleConnectingState();
+ void HandleConnectedState();
+ void HandleReadingState();
+ void HandleReadDoneState();
+ void HandleWritingState();
+ void HandleWriteDoneState();
+ void HandleReadingAckState();
+ void HandleDisconnectingState();
+
+ // Prepares reply for a client from the given parameters.
+ bool PrepareReply(const ClientInfo& client_info,
+ ProtocolMessage* reply) const;
+
+ // Duplicates various handles in the ClientInfo object for the client
+ // process and stores them in the given ProtocolMessage instance. If
+ // creating any handle fails, ProtocolMessage will contain the handles
+ // already created successfully, which should be closed by the caller.
+ bool CreateClientHandles(const ClientInfo& client_info,
+ ProtocolMessage* reply) const;
+
+ // Response to the given client. Return true if all steps of
+ // responding to the client succeed, false otherwise.
+ bool RespondToClient(ClientInfo* client_info);
+
+ // Handles a connection request from the client.
+ void HandleConnectionRequest();
+
+ // Handles a dump request from the client.
+ void HandleDumpRequest(const ClientInfo& client_info);
+
+ // Callback for pipe connected event.
+ static void CALLBACK OnPipeConnected(void* context, BOOLEAN timer_or_wait);
+
+ // Callback for a dump request.
+ static void CALLBACK OnDumpRequest(void* context, BOOLEAN timer_or_wait);
+
+ // Callback for client process exit event.
+ static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait);
+
+ // Handles client process exit.
+ void HandleClientProcessExit(ClientInfo* client_info);
+
+ // Adds the given client to the list of registered clients.
+ bool AddClient(ClientInfo* client_info);
+
+ // Generates dump for the given client.
+ bool GenerateDump(const ClientInfo& client, std::wstring* dump_path);
+
+ // Puts the server in a permanent error state and sets a signal such that
+ // the state will be immediately entered after the current state transition
+ // is complete.
+ void EnterErrorState();
+
+ // Puts the server in the specified state and sets a signal such that the
+ // state is immediately entered after the current state transition is
+ // complete.
+ void EnterStateImmediately(IPCServerState state);
+
+ // Puts the server in the specified state. No signal will be set, so the state
+ // transition will only occur when signaled manually or by completion of an
+ // asynchronous IO operation.
+ void EnterStateWhenSignaled(IPCServerState state);
+
+ // Sync object for thread-safe access to the shared list of clients.
+ CRITICAL_SECTION sync_;
+
+ // List of clients.
+ std::list<ClientInfo*> clients_;
+
+ // Pipe name.
+ std::wstring pipe_name_;
+
+ // Pipe security attributes
+ SECURITY_ATTRIBUTES* pipe_sec_attrs_;
+
+ // Handle to the pipe used for handshake with clients.
+ HANDLE pipe_;
+
+ // Pipe wait handle.
+ HANDLE pipe_wait_handle_;
+
+ // Handle to server-alive mutex.
+ HANDLE server_alive_handle_;
+
+ // Callback for a successful client connection.
+ OnClientConnectedCallback connect_callback_;
+
+ // Context for client connected callback.
+ void* connect_context_;
+
+ // Callback for a client dump request.
+ OnClientDumpRequestCallback dump_callback_;
+
+ // Context for client dump request callback.
+ void* dump_context_;
+
+ // Callback for a client dump written.
+ OnClientDumpWrittenCallback written_callback_;
+
+ // Callback for client process exit.
+ OnClientExitedCallback exit_callback_;
+
+ // Context for client process exit callback.
+ void* exit_context_;
+
+ // Callback for upload request.
+ OnClientUploadRequestCallback upload_request_callback_;
+
+ // Context for upload request callback.
+ void* upload_context_;
+
+ // Whether to generate dumps.
+ bool generate_dumps_;
+
+ // Wether to populate custom information up-front.
+ bool pre_fetch_custom_info_;
+
+ // The dump path for the server.
+ const std::wstring dump_path_;
+
+ // State of the server in performing the IPC with the client.
+ // Note that since we restrict the pipe to one instance, we
+ // only need to keep one state of the server. Otherwise, server
+ // would have one state per client it is talking to.
+ IPCServerState server_state_;
+
+ // Whether the server is shutting down.
+ bool shutting_down_;
+
+ // Overlapped instance for async I/O on the pipe.
+ OVERLAPPED overlapped_;
+
+ // Message object used in IPC with the client.
+ ProtocolMessage msg_;
+
+ // Client Info for the client that's connecting to the server.
+ ClientInfo* client_info_;
+
+ // Whether to include heap regions of the crashing context.
+ bool include_context_heap_;
+
+ AppMemoryList app_memory_info_;
+
+ // Disable copy ctor and operator=.
+ CrashGenerationServer(const CrashGenerationServer& crash_server);
+ CrashGenerationServer& operator=(const CrashGenerationServer& crash_server);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.cc b/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.cc
new file mode 100644
index 0000000000..0e0643c5a1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.cc
@@ -0,0 +1,581 @@
+// Copyright (c) 2008, 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 "windows/crash_generation/minidump_generator.h"
+
+#include <assert.h>
+#include <avrfsdk.h>
+
+#include <algorithm>
+#include <iterator>
+#include <list>
+#include <vector>
+
+#include "windows/common/auto_critical_section.h"
+#include "common/scoped_ptr.h"
+#include "common/windows/guid_string.h"
+
+using std::wstring;
+
+namespace {
+
+// A helper class used to collect handle operations data. Unlike
+// |MiniDumpWithHandleData| it records the operations for a single handle value
+// only, making it possible to include this information to a minidump.
+class HandleTraceData {
+ public:
+ HandleTraceData();
+ ~HandleTraceData();
+
+ // Collects the handle operations data and formats a user stream to be added
+ // to the minidump.
+ bool CollectHandleData(HANDLE process_handle,
+ EXCEPTION_POINTERS* exception_pointers);
+
+ // Fills the user dump entry with a pointer to the collected handle operations
+ // data. Returns |true| if the entry was initialized successfully, or |false|
+ // if no trace data is available.
+ bool GetUserStream(MINIDUMP_USER_STREAM* user_stream);
+
+ private:
+ // Reads the exception code from the client process's address space.
+ // This routine assumes that the client process's pointer width matches ours.
+ static bool ReadExceptionCode(HANDLE process_handle,
+ EXCEPTION_POINTERS* exception_pointers,
+ DWORD* exception_code);
+
+ // Stores handle operations retrieved by VerifierEnumerateResource().
+ static ULONG CALLBACK RecordHandleOperations(void* resource_description,
+ void* enumeration_context,
+ ULONG* enumeration_level);
+
+ // Function pointer type for VerifierEnumerateResource, which is looked up
+ // dynamically.
+ typedef BOOL (WINAPI* VerifierEnumerateResourceType)(
+ HANDLE Process,
+ ULONG Flags,
+ ULONG ResourceType,
+ AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback,
+ PVOID EnumerationContext);
+
+ // Handle to dynamically loaded verifier.dll.
+ HMODULE verifier_module_;
+
+ // Pointer to the VerifierEnumerateResource function.
+ VerifierEnumerateResourceType enumerate_resource_;
+
+ // Handle value to look for.
+ ULONG64 handle_;
+
+ // List of handle operations for |handle_|.
+ std::list<AVRF_HANDLE_OPERATION> operations_;
+
+ // Minidump stream data.
+ std::vector<char> stream_;
+};
+
+HandleTraceData::HandleTraceData()
+ : verifier_module_(NULL),
+ enumerate_resource_(NULL),
+ handle_(NULL) {
+}
+
+HandleTraceData::~HandleTraceData() {
+ if (verifier_module_) {
+ FreeLibrary(verifier_module_);
+ }
+}
+
+bool HandleTraceData::CollectHandleData(
+ HANDLE process_handle,
+ EXCEPTION_POINTERS* exception_pointers) {
+ DWORD exception_code;
+ if (!ReadExceptionCode(process_handle, exception_pointers, &exception_code)) {
+ return false;
+ }
+
+ // Verify whether the execption is STATUS_INVALID_HANDLE. Do not record any
+ // handle information if it is a different exception to keep the minidump
+ // small.
+ if (exception_code != STATUS_INVALID_HANDLE) {
+ return true;
+ }
+
+ // Load verifier!VerifierEnumerateResource() dynamically.
+ verifier_module_ = LoadLibrary(TEXT("verifier.dll"));
+ if (!verifier_module_) {
+ return false;
+ }
+
+ enumerate_resource_ = reinterpret_cast<VerifierEnumerateResourceType>(
+ GetProcAddress(verifier_module_, "VerifierEnumerateResource"));
+ if (!enumerate_resource_) {
+ return false;
+ }
+
+ // STATUS_INVALID_HANDLE does not provide the offending handle value in
+ // the exception parameters so we have to guess. At the moment we scan
+ // the handle operations trace looking for the last invalid handle operation
+ // and record only the operations for that handle value.
+ if (enumerate_resource_(process_handle,
+ 0,
+ AvrfResourceHandleTrace,
+ &RecordHandleOperations,
+ this) != ERROR_SUCCESS) {
+ // The handle tracing must have not been enabled.
+ return true;
+ }
+
+ // Now that |handle_| is initialized, purge all irrelevant operations.
+ std::list<AVRF_HANDLE_OPERATION>::iterator i = operations_.begin();
+ std::list<AVRF_HANDLE_OPERATION>::iterator i_end = operations_.end();
+ while (i != i_end) {
+ if (i->Handle == handle_) {
+ ++i;
+ } else {
+ i = operations_.erase(i);
+ }
+ }
+
+ // Convert the list of recorded operations to a minidump stream.
+ stream_.resize(sizeof(MINIDUMP_HANDLE_OPERATION_LIST) +
+ sizeof(AVRF_HANDLE_OPERATION) * operations_.size());
+
+ MINIDUMP_HANDLE_OPERATION_LIST* stream_data =
+ reinterpret_cast<MINIDUMP_HANDLE_OPERATION_LIST*>(
+ &stream_.front());
+ stream_data->SizeOfHeader = sizeof(MINIDUMP_HANDLE_OPERATION_LIST);
+ stream_data->SizeOfEntry = sizeof(AVRF_HANDLE_OPERATION);
+ stream_data->NumberOfEntries = static_cast<ULONG32>(operations_.size());
+ stream_data->Reserved = 0;
+ std::copy(operations_.begin(),
+ operations_.end(),
+#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER)
+ stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
+ reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
+ operations_.size())
+#else
+ reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1)
+#endif
+ );
+
+ return true;
+}
+
+bool HandleTraceData::GetUserStream(MINIDUMP_USER_STREAM* user_stream) {
+ if (stream_.empty()) {
+ return false;
+ } else {
+ user_stream->Type = HandleOperationListStream;
+ user_stream->BufferSize = static_cast<ULONG>(stream_.size());
+ user_stream->Buffer = &stream_.front();
+ return true;
+ }
+}
+
+bool HandleTraceData::ReadExceptionCode(
+ HANDLE process_handle,
+ EXCEPTION_POINTERS* exception_pointers,
+ DWORD* exception_code) {
+ EXCEPTION_POINTERS pointers;
+ if (!ReadProcessMemory(process_handle,
+ exception_pointers,
+ &pointers,
+ sizeof(pointers),
+ NULL)) {
+ return false;
+ }
+
+ if (!ReadProcessMemory(process_handle,
+ pointers.ExceptionRecord,
+ exception_code,
+ sizeof(*exception_code),
+ NULL)) {
+ return false;
+ }
+
+ return true;
+}
+
+ULONG CALLBACK HandleTraceData::RecordHandleOperations(
+ void* resource_description,
+ void* enumeration_context,
+ ULONG* enumeration_level) {
+ AVRF_HANDLE_OPERATION* description =
+ reinterpret_cast<AVRF_HANDLE_OPERATION*>(resource_description);
+ HandleTraceData* self =
+ reinterpret_cast<HandleTraceData*>(enumeration_context);
+
+ // Remember the last invalid handle operation.
+ if (description->OperationType == OperationDbBADREF) {
+ self->handle_ = description->Handle;
+ }
+
+ // Record all handle operations.
+ self->operations_.push_back(*description);
+
+ *enumeration_level = HeapEnumerationEverything;
+ return ERROR_SUCCESS;
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+MinidumpGenerator::MinidumpGenerator(
+ const std::wstring& dump_path,
+ const HANDLE process_handle,
+ const DWORD process_id,
+ const DWORD thread_id,
+ const DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exception_pointers,
+ MDRawAssertionInfo* assert_info,
+ const MINIDUMP_TYPE dump_type,
+ const bool is_client_pointers)
+ : dbghelp_module_(NULL),
+ write_dump_(NULL),
+ rpcrt4_module_(NULL),
+ create_uuid_(NULL),
+ process_handle_(process_handle),
+ process_id_(process_id),
+ thread_id_(thread_id),
+ requesting_thread_id_(requesting_thread_id),
+ exception_pointers_(exception_pointers),
+ assert_info_(assert_info),
+ dump_type_(dump_type),
+ is_client_pointers_(is_client_pointers),
+ dump_path_(dump_path),
+ uuid_generated_(false),
+ dump_file_(INVALID_HANDLE_VALUE),
+ full_dump_file_(INVALID_HANDLE_VALUE),
+ dump_file_is_internal_(false),
+ full_dump_file_is_internal_(false),
+ additional_streams_(NULL),
+ callback_info_(NULL) {
+ uuid_ = {0};
+ InitializeCriticalSection(&module_load_sync_);
+ InitializeCriticalSection(&get_proc_address_sync_);
+}
+
+MinidumpGenerator::~MinidumpGenerator() {
+ if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(dump_file_);
+ }
+
+ if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(full_dump_file_);
+ }
+
+ if (dbghelp_module_) {
+ FreeLibrary(dbghelp_module_);
+ }
+
+ if (rpcrt4_module_) {
+ FreeLibrary(rpcrt4_module_);
+ }
+
+ DeleteCriticalSection(&get_proc_address_sync_);
+ DeleteCriticalSection(&module_load_sync_);
+}
+
+bool MinidumpGenerator::WriteMinidump() {
+ bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0;
+ if (dump_file_ == INVALID_HANDLE_VALUE ||
+ (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) {
+ return false;
+ }
+
+ MiniDumpWriteDumpType write_dump = GetWriteDump();
+ if (!write_dump) {
+ return false;
+ }
+
+ MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
+
+ // Setup the exception information object only if it's a dump
+ // due to an exception.
+ if (exception_pointers_) {
+ dump_exception_info.ThreadId = thread_id_;
+ dump_exception_info.ExceptionPointers = exception_pointers_;
+ dump_exception_info.ClientPointers = is_client_pointers_;
+ }
+
+ // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
+ // information about the exception handler to the Breakpad processor.
+ // The information will help the processor determine which threads are
+ // relevant. The Breakpad processor does not require this information but
+ // can function better with Breakpad-generated dumps when it is present.
+ // The native debugger is not harmed by the presence of this information.
+ MDRawBreakpadInfo breakpad_info = {0};
+ if (!is_client_pointers_) {
+ // Set the dump thread id and requesting thread id only in case of
+ // in-process dump generation.
+ breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
+ breakpad_info.dump_thread_id = thread_id_;
+ breakpad_info.requesting_thread_id = requesting_thread_id_;
+ }
+
+ int additional_streams_count = additional_streams_ ?
+ additional_streams_->UserStreamCount : 0;
+ scoped_array<MINIDUMP_USER_STREAM> user_stream_array(
+ new MINIDUMP_USER_STREAM[3 + additional_streams_count]);
+ user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
+ user_stream_array[0].BufferSize = sizeof(breakpad_info);
+ user_stream_array[0].Buffer = &breakpad_info;
+
+ MINIDUMP_USER_STREAM_INFORMATION user_streams;
+ user_streams.UserStreamCount = 1;
+ user_streams.UserStreamArray = user_stream_array.get();
+
+ MDRawAssertionInfo* actual_assert_info = assert_info_;
+ MDRawAssertionInfo client_assert_info = {{0}};
+
+ if (assert_info_) {
+ // If the assertion info object lives in the client process,
+ // read the memory of the client process.
+ if (is_client_pointers_) {
+ SIZE_T bytes_read = 0;
+ if (!ReadProcessMemory(process_handle_,
+ assert_info_,
+ &client_assert_info,
+ sizeof(client_assert_info),
+ &bytes_read)) {
+ if (dump_file_is_internal_)
+ CloseHandle(dump_file_);
+ if (full_dump_file_is_internal_ &&
+ full_dump_file_ != INVALID_HANDLE_VALUE)
+ CloseHandle(full_dump_file_);
+ return false;
+ }
+
+ if (bytes_read != sizeof(client_assert_info)) {
+ if (dump_file_is_internal_)
+ CloseHandle(dump_file_);
+ if (full_dump_file_is_internal_ &&
+ full_dump_file_ != INVALID_HANDLE_VALUE)
+ CloseHandle(full_dump_file_);
+ return false;
+ }
+
+ actual_assert_info = &client_assert_info;
+ }
+
+ user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
+ user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
+ user_stream_array[1].Buffer = actual_assert_info;
+ ++user_streams.UserStreamCount;
+ }
+
+ if (additional_streams_) {
+ for (size_t i = 0;
+ i < additional_streams_->UserStreamCount;
+ i++, user_streams.UserStreamCount++) {
+ user_stream_array[user_streams.UserStreamCount].Type =
+ additional_streams_->UserStreamArray[i].Type;
+ user_stream_array[user_streams.UserStreamCount].BufferSize =
+ additional_streams_->UserStreamArray[i].BufferSize;
+ user_stream_array[user_streams.UserStreamCount].Buffer =
+ additional_streams_->UserStreamArray[i].Buffer;
+ }
+ }
+
+ // If the process is terminated by STATUS_INVALID_HANDLE exception store
+ // the trace of operations for the offending handle value. Do nothing special
+ // if the client already requested the handle trace to be stored in the dump.
+ HandleTraceData handle_trace_data;
+ if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) {
+ if (!handle_trace_data.CollectHandleData(process_handle_,
+ exception_pointers_)) {
+ if (dump_file_is_internal_)
+ CloseHandle(dump_file_);
+ if (full_dump_file_is_internal_ &&
+ full_dump_file_ != INVALID_HANDLE_VALUE)
+ CloseHandle(full_dump_file_);
+ return false;
+ }
+ }
+
+ bool result_full_memory = true;
+ if (full_memory_dump) {
+ result_full_memory = write_dump(
+ process_handle_,
+ process_id_,
+ full_dump_file_,
+ static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal))
+ | MiniDumpWithHandleData),
+ exception_pointers_ ? &dump_exception_info : NULL,
+ &user_streams,
+ NULL) != FALSE;
+ }
+
+ // Add handle operations trace stream to the minidump if it was collected.
+ if (handle_trace_data.GetUserStream(
+ &user_stream_array[user_streams.UserStreamCount])) {
+ ++user_streams.UserStreamCount;
+ }
+
+ bool result_minidump = write_dump(
+ process_handle_,
+ process_id_,
+ dump_file_,
+ static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory))
+ | MiniDumpNormal),
+ exception_pointers_ ? &dump_exception_info : NULL,
+ &user_streams,
+ callback_info_) != FALSE;
+
+ return result_minidump && result_full_memory;
+}
+
+bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) {
+ // The dump file was already set by handle or this function was previously
+ // called.
+ if (dump_file_ != INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ wstring dump_file_path;
+ if (!GenerateDumpFilePath(&dump_file_path)) {
+ return false;
+ }
+
+ dump_file_ = CreateFile(dump_file_path.c_str(),
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (dump_file_ == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ dump_file_is_internal_ = true;
+ *dump_path = dump_file_path;
+ return true;
+}
+
+bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) {
+ // A full minidump was not requested.
+ if ((dump_type_ & MiniDumpWithFullMemory) == 0) {
+ return false;
+ }
+
+ // The dump file was already set by handle or this function was previously
+ // called.
+ if (full_dump_file_ != INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ wstring full_dump_file_path;
+ if (!GenerateDumpFilePath(&full_dump_file_path)) {
+ return false;
+ }
+ full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
+ full_dump_file_path.append(TEXT("-full.dmp"));
+
+ full_dump_file_ = CreateFile(full_dump_file_path.c_str(),
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (full_dump_file_ == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ full_dump_file_is_internal_ = true;
+ *full_dump_path = full_dump_file_path;
+ return true;
+}
+
+HMODULE MinidumpGenerator::GetDbghelpModule() {
+ AutoCriticalSection lock(&module_load_sync_);
+ if (!dbghelp_module_) {
+ dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
+ }
+
+ return dbghelp_module_;
+}
+
+MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
+ AutoCriticalSection lock(&get_proc_address_sync_);
+ if (!write_dump_) {
+ HMODULE module = GetDbghelpModule();
+ if (module) {
+ FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
+ write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
+ }
+ }
+
+ return write_dump_;
+}
+
+HMODULE MinidumpGenerator::GetRpcrt4Module() {
+ AutoCriticalSection lock(&module_load_sync_);
+ if (!rpcrt4_module_) {
+ rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
+ }
+
+ return rpcrt4_module_;
+}
+
+MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
+ AutoCriticalSection lock(&module_load_sync_);
+ if (!create_uuid_) {
+ HMODULE module = GetRpcrt4Module();
+ if (module) {
+ FARPROC proc = GetProcAddress(module, "UuidCreate");
+ create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
+ }
+ }
+
+ return create_uuid_;
+}
+
+bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
+ if (!uuid_generated_) {
+ UuidCreateType create_uuid = GetCreateUuid();
+ if (!create_uuid) {
+ return false;
+ }
+
+ create_uuid(&uuid_);
+ uuid_generated_ = true;
+ }
+
+ wstring id_str = GUIDString::GUIDToWString(&uuid_);
+
+ *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.h b/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.h
new file mode 100644
index 0000000000..a707c0bb1d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/minidump_generator.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2008, 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.
+
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
+#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <rpc.h>
+#include <list>
+#include <string>
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+// Abstraction for various objects and operations needed to generate
+// minidump on Windows. This abstraction is useful to hide all the gory
+// details for minidump generation and provide a clean interface to
+// the clients to generate minidumps.
+class MinidumpGenerator {
+ public:
+ // Creates an instance with the given parameters.
+ // is_client_pointers specifies whether the exception_pointers and
+ // assert_info point into the process that is being dumped.
+ // Before calling WriteMinidump on the returned instance a dump file muct be
+ // specified by a call to either SetDumpFile() or GenerateDumpFile().
+ // If a full dump file will be requested via a subsequent call to either
+ // SetFullDumpFile or GenerateFullDumpFile() dump_type must include
+ // MiniDumpWithFullMemory.
+ MinidumpGenerator(const std::wstring& dump_path,
+ const HANDLE process_handle,
+ const DWORD process_id,
+ const DWORD thread_id,
+ const DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exception_pointers,
+ MDRawAssertionInfo* assert_info,
+ const MINIDUMP_TYPE dump_type,
+ const bool is_client_pointers);
+
+ ~MinidumpGenerator();
+
+ void SetDumpFile(const HANDLE dump_file) { dump_file_ = dump_file; }
+ void SetFullDumpFile(const HANDLE full_dump_file) {
+ full_dump_file_ = full_dump_file;
+ }
+
+ // Generate the name for the dump file that will be written to once
+ // WriteMinidump() is called. Can only be called once and cannot be called
+ // if the dump file is set via SetDumpFile().
+ bool GenerateDumpFile(std::wstring* dump_path);
+
+ // Generate the name for the full dump file that will be written to once
+ // WriteMinidump() is called. Cannot be called unless the minidump type
+ // includes MiniDumpWithFullMemory. Can only be called once and cannot be
+ // called if the dump file is set via SetFullDumpFile().
+ bool GenerateFullDumpFile(std::wstring* full_dump_path);
+
+ void SetAdditionalStreams(
+ MINIDUMP_USER_STREAM_INFORMATION* additional_streams) {
+ additional_streams_ = additional_streams;
+ }
+
+ void SetCallback(MINIDUMP_CALLBACK_INFORMATION* callback_info) {
+ callback_info_ = callback_info;
+ }
+
+ // Writes the minidump with the given parameters. Stores the
+ // dump file path in the dump_path parameter if dump generation
+ // succeeds.
+ bool WriteMinidump();
+
+ private:
+ // Function pointer type for MiniDumpWriteDump, which is looked up
+ // dynamically.
+ typedef BOOL (WINAPI* MiniDumpWriteDumpType)(
+ HANDLE hProcess,
+ DWORD ProcessId,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+
+ // Function pointer type for UuidCreate, which is looked up dynamically.
+ typedef RPC_STATUS (RPC_ENTRY* UuidCreateType)(UUID* Uuid);
+
+ // Loads the appropriate DLL lazily in a thread safe way.
+ HMODULE GetDbghelpModule();
+
+ // Loads the appropriate DLL and gets a pointer to the MiniDumpWriteDump
+ // function lazily and in a thread-safe manner.
+ MiniDumpWriteDumpType GetWriteDump();
+
+ // Loads the appropriate DLL lazily in a thread safe way.
+ HMODULE GetRpcrt4Module();
+
+ // Loads the appropriate DLL and gets a pointer to the UuidCreate
+ // function lazily and in a thread-safe manner.
+ UuidCreateType GetCreateUuid();
+
+ // Returns the path for the file to write dump to.
+ bool GenerateDumpFilePath(std::wstring* file_path);
+
+ // Handle to dynamically loaded DbgHelp.dll.
+ HMODULE dbghelp_module_;
+
+ // Pointer to the MiniDumpWriteDump function.
+ MiniDumpWriteDumpType write_dump_;
+
+ // Handle to dynamically loaded rpcrt4.dll.
+ HMODULE rpcrt4_module_;
+
+ // Pointer to the UuidCreate function.
+ UuidCreateType create_uuid_;
+
+ // Handle for the process to dump.
+ HANDLE process_handle_;
+
+ // Process ID for the process to dump.
+ DWORD process_id_;
+
+ // The crashing thread ID.
+ DWORD thread_id_;
+
+ // The thread ID which is requesting the dump.
+ DWORD requesting_thread_id_;
+
+ // Pointer to the exception information for the crash. This may point to an
+ // address in the crashing process so it should not be dereferenced.
+ EXCEPTION_POINTERS* exception_pointers_;
+
+ // Assertion info for the report.
+ MDRawAssertionInfo* assert_info_;
+
+ // Type of minidump to generate.
+ MINIDUMP_TYPE dump_type_;
+
+ // Specifies whether the exception_pointers_ reference memory in the crashing
+ // process.
+ bool is_client_pointers_;
+
+ // Folder path to store dump files.
+ std::wstring dump_path_;
+
+ // UUID used to make dump file names.
+ UUID uuid_;
+ bool uuid_generated_;
+
+ // The file where the dump will be written.
+ HANDLE dump_file_;
+
+ // The file where the full dump will be written.
+ HANDLE full_dump_file_;
+
+ // Tracks whether the dump file handle is managed externally.
+ bool dump_file_is_internal_;
+
+ // Tracks whether the full dump file handle is managed externally.
+ bool full_dump_file_is_internal_;
+
+ // Additional streams to be written to the dump.
+ MINIDUMP_USER_STREAM_INFORMATION* additional_streams_;
+
+ // The user defined callback for the various stages of the dump process.
+ MINIDUMP_CALLBACK_INFORMATION* callback_info_;
+
+ // Critical section to sychronize action of loading modules dynamically.
+ CRITICAL_SECTION module_load_sync_;
+
+ // Critical section to synchronize action of dynamically getting function
+ // addresses from modules.
+ CRITICAL_SECTION get_proc_address_sync_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
diff --git a/toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild b/toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild
new file mode 100644
index 0000000000..c964936f1d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+lobjs_crash_generation = [
+ 'client_info.cc',
+ 'crash_generation_client.cc',
+ 'crash_generation_server.cc',
+ 'minidump_generator.cc',
+]
+
+subdir = 'toolkit/crashreporter/breakpad-client/windows/crash_generation'
+objs_crash_generation = [
+ '/%s/%s' % (subdir, s) for s in lobjs_crash_generation
+]
diff --git a/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.cc b/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.cc
new file mode 100644
index 0000000000..f87adba34b
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.cc
@@ -0,0 +1,1121 @@
+// 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 <objbase.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+
+#include "common/windows/string_utils-inl.h"
+
+#include "windows/common/ipc_protocol.h"
+#include "windows/handler/exception_handler.h"
+#include "common/windows/guid_string.h"
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#endif
+
+namespace google_breakpad {
+
+// This define is new to Windows 10.
+#ifndef DBG_PRINTEXCEPTION_WIDE_C
+#define DBG_PRINTEXCEPTION_WIDE_C ((DWORD)0x4001000A)
+#endif
+
+vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
+LONG ExceptionHandler::handler_stack_index_ = 0;
+CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
+volatile LONG ExceptionHandler::instance_count_ = 0;
+
+ExceptionHandler::ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ const CustomClientInfo* custom_info) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ dump_type,
+ pipe_name,
+ NULL, // pipe_handle
+ NULL, // crash_generation_client
+ custom_info);
+}
+
+ExceptionHandler::ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ HANDLE pipe_handle,
+ const CustomClientInfo* custom_info) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ dump_type,
+ NULL, // pipe_name
+ pipe_handle,
+ NULL, // crash_generation_client
+ custom_info);
+}
+
+ExceptionHandler::ExceptionHandler(
+ const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ CrashGenerationClient* crash_generation_client) {
+ // The dump_type, pipe_name and custom_info that are passed in to Initialize()
+ // are not used. The ones set in crash_generation_client are used instead.
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ MiniDumpNormal, // dump_type - not used
+ NULL, // pipe_name - not used
+ NULL, // pipe_handle
+ crash_generation_client,
+ NULL); // custom_info - not used
+}
+
+ExceptionHandler::ExceptionHandler(const wstring &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types) {
+ Initialize(dump_path,
+ filter,
+ callback,
+ callback_context,
+ handler_types,
+ MiniDumpNormal,
+ NULL, // pipe_name
+ NULL, // pipe_handle
+ NULL, // crash_generation_client
+ NULL); // custom_info
+}
+
+void ExceptionHandler::Initialize(
+ const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ HANDLE pipe_handle,
+ CrashGenerationClient* crash_generation_client,
+ const CustomClientInfo* custom_info) {
+ LONG instance_count = InterlockedIncrement(&instance_count_);
+ filter_ = filter;
+ callback_ = callback;
+ callback_context_ = callback_context;
+ dump_path_c_ = NULL;
+ next_minidump_id_c_ = NULL;
+ next_minidump_path_c_ = NULL;
+ dbghelp_module_ = NULL;
+ minidump_write_dump_ = NULL;
+ dump_type_ = dump_type;
+ rpcrt4_module_ = NULL;
+ uuid_create_ = NULL;
+ handler_types_ = handler_types;
+ previous_filter_ = NULL;
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ previous_iph_ = NULL;
+#endif // _MSC_VER >= 1400
+ previous_pch_ = NULL;
+ heap_corruption_veh_ = NULL;
+ handler_thread_ = NULL;
+ is_shutdown_ = false;
+ handler_start_semaphore_ = NULL;
+ handler_finish_semaphore_ = NULL;
+ requesting_thread_id_ = 0;
+ exception_info_ = NULL;
+ assertion_ = NULL;
+ handler_return_value_ = MinidumpResult::Failure;
+ handle_debug_exceptions_ = false;
+ consume_invalid_handle_exceptions_ = false;
+
+ // Attempt to use out-of-process if user has specified a pipe or a
+ // crash generation client.
+ scoped_ptr<CrashGenerationClient> client;
+ if (crash_generation_client) {
+ client.reset(crash_generation_client);
+ } else if (pipe_name) {
+ client.reset(
+ new CrashGenerationClient(pipe_name, dump_type_, custom_info));
+ } else if (pipe_handle) {
+ client.reset(
+ new CrashGenerationClient(pipe_handle, dump_type_, custom_info));
+ }
+
+ if (client.get() != NULL) {
+ // If successful in registering with the monitoring process,
+ // there is no need to setup in-process crash generation.
+ if (client->Register()) {
+ crash_generation_client_.reset(client.release());
+ }
+ }
+
+ if (!IsOutOfProcess()) {
+ // Either client did not ask for out-of-process crash generation
+ // or registration with the server process failed. In either case,
+ // setup to do in-process crash generation.
+
+ // Set synchronization primitives and the handler thread. Each
+ // ExceptionHandler object gets its own handler thread because that's the
+ // only way to reliably guarantee sufficient stack space in an exception,
+ // and it allows an easy way to get a snapshot of the requesting thread's
+ // context outside of an exception.
+ InitializeCriticalSection(&handler_critical_section_);
+ handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
+ assert(handler_start_semaphore_ != NULL);
+
+ handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
+ assert(handler_finish_semaphore_ != NULL);
+
+ // Don't attempt to create the thread if we could not create the semaphores.
+ if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {
+ DWORD thread_id;
+ const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
+ handler_thread_ = CreateThread(NULL, // lpThreadAttributes
+ kExceptionHandlerThreadInitialStackSize,
+ ExceptionHandlerThreadMain,
+ this, // lpParameter
+ 0, // dwCreationFlags
+ &thread_id);
+ assert(handler_thread_ != NULL);
+ }
+
+ dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
+ if (dbghelp_module_) {
+ minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(
+ GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
+ }
+
+ // Load this library dynamically to not affect existing projects. Most
+ // projects don't link against this directly, it's usually dynamically
+ // loaded by dependent code.
+ rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll");
+ if (rpcrt4_module_) {
+ uuid_create_ = reinterpret_cast<UuidCreate_type>(
+ GetProcAddress(rpcrt4_module_, "UuidCreate"));
+ }
+
+ // set_dump_path calls UpdateNextID. This sets up all of the path and id
+ // strings, and their equivalent c_str pointers.
+ set_dump_path(dump_path);
+ }
+
+ // Reserve one element for the instruction memory
+ AppMemory instruction_memory;
+ instruction_memory.ptr = NULL;
+ instruction_memory.length = 0;
+ instruction_memory.preallocated = true;
+ app_memory_info_.push_back(instruction_memory);
+
+ // There is a race condition here. If the first instance has not yet
+ // initialized the critical section, the second (and later) instances may
+ // try to use uninitialized critical section object. The feature of multiple
+ // instances in one module is not used much, so leave it as is for now.
+ // One way to solve this in the current design (that is, keeping the static
+ // handler stack) is to use spin locks with volatile bools to synchronize
+ // the handler stack. This works only if the compiler guarantees to generate
+ // cache coherent code for volatile.
+ // TODO(munjal): Fix this in a better way by changing the design if possible.
+
+ // Lazy initialization of the handler_stack_critical_section_
+ if (instance_count == 1) {
+ InitializeCriticalSection(&handler_stack_critical_section_);
+ }
+
+ if (handler_types != HANDLER_NONE) {
+ EnterCriticalSection(&handler_stack_critical_section_);
+
+ // The first time an ExceptionHandler that installs a handler is
+ // created, set up the handler stack.
+ if (!handler_stack_) {
+ handler_stack_ = new vector<ExceptionHandler*>();
+ }
+ handler_stack_->push_back(this);
+
+ if (handler_types & HANDLER_EXCEPTION)
+ previous_filter_ = SetUnhandledExceptionFilter(HandleException);
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if (handler_types & HANDLER_INVALID_PARAMETER)
+ previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
+#endif // _MSC_VER >= 1400
+
+ if (handler_types & HANDLER_PURECALL)
+ previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);
+
+ if (handler_types & HANDLER_HEAP_CORRUPTION) {
+ // Under ASan we need to let the ASan runtime's ShadowExceptionHandler stay
+ // in the first handler position.
+#ifdef MOZ_ASAN
+ const bool first = false;
+#else
+ const bool first = true;
+#endif // MOZ_ASAN
+ heap_corruption_veh_ =
+ AddVectoredExceptionHandler(first, HandleHeapCorruption);
+ }
+
+ LeaveCriticalSection(&handler_stack_critical_section_);
+ }
+
+ include_context_heap_ = false;
+}
+
+ExceptionHandler::~ExceptionHandler() {
+ if (handler_types_ != HANDLER_NONE) {
+ EnterCriticalSection(&handler_stack_critical_section_);
+
+ if (handler_types_ & HANDLER_EXCEPTION)
+ SetUnhandledExceptionFilter(previous_filter_);
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if (handler_types_ & HANDLER_INVALID_PARAMETER)
+ _set_invalid_parameter_handler(previous_iph_);
+#endif // _MSC_VER >= 1400
+
+ if (handler_types_ & HANDLER_PURECALL)
+ _set_purecall_handler(previous_pch_);
+
+ if (handler_types_ & HANDLER_HEAP_CORRUPTION)
+ RemoveVectoredExceptionHandler(heap_corruption_veh_);
+
+ if (handler_stack_->back() == this) {
+ handler_stack_->pop_back();
+ } else {
+ // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the
+ // system's application event log.
+ fprintf(stderr, "warning: removing Breakpad handler out of order\n");
+ vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
+ while (iterator != handler_stack_->end()) {
+ if (*iterator == this) {
+ iterator = handler_stack_->erase(iterator);
+ } else {
+ ++iterator;
+ }
+ }
+ }
+
+ if (handler_stack_->empty()) {
+ // When destroying the last ExceptionHandler that installed a handler,
+ // clean up the handler stack.
+ delete handler_stack_;
+ handler_stack_ = NULL;
+ }
+
+ LeaveCriticalSection(&handler_stack_critical_section_);
+ }
+
+ // Some of the objects were only initialized if out of process
+ // registration was not done.
+ if (!IsOutOfProcess()) {
+#ifdef BREAKPAD_NO_TERMINATE_THREAD
+ // Clean up the handler thread and synchronization primitives. The handler
+ // thread is either waiting on the semaphore to handle a crash or it is
+ // handling a crash. Coming out of the wait is fast but wait more in the
+ // eventuality a crash is handled. This compilation option results in a
+ // deadlock if the exception handler is destroyed while executing code
+ // inside DllMain.
+ is_shutdown_ = true;
+ ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
+ const int kWaitForHandlerThreadMs = 60000;
+ WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
+#else
+ TerminateThread(handler_thread_, 1);
+#endif // BREAKPAD_NO_TERMINATE_THREAD
+
+ CloseHandle(handler_thread_);
+ handler_thread_ = NULL;
+ DeleteCriticalSection(&handler_critical_section_);
+ CloseHandle(handler_start_semaphore_);
+ CloseHandle(handler_finish_semaphore_);
+ }
+
+ // There is a race condition in the code below: if this instance is
+ // deleting the static critical section and a new instance of the class
+ // is created, then there is a possibility that the critical section be
+ // initialized while the same critical section is being deleted. Given the
+ // usage pattern for the code, this race condition is unlikely to hit, but it
+ // is a race condition nonetheless.
+ if (InterlockedDecrement(&instance_count_) == 0) {
+ DeleteCriticalSection(&handler_stack_critical_section_);
+ }
+
+ // The exception handler is not set anymore and the handler thread which
+ // could call MiniDumpWriteDump() has been shut down; it is now safe to
+ // unload these modules.
+ if (dbghelp_module_) {
+ FreeLibrary(dbghelp_module_);
+ }
+
+ if (rpcrt4_module_) {
+ FreeLibrary(rpcrt4_module_);
+ }
+}
+
+bool ExceptionHandler::RequestUpload(DWORD crash_id) {
+ return crash_generation_client_->RequestUpload(crash_id);
+}
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescriptionPtr)(HANDLE hThread,
+ PCWSTR lpThreadDescription);
+
+// static
+DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) {
+ HMODULE handle = ::GetModuleHandle(L"Kernel32.dll");
+ if (handle) {
+ if (FARPROC address = ::GetProcAddress(handle, "SetThreadDescription")) {
+ auto SetThreadDescriptionFunc =
+ reinterpret_cast<SetThreadDescriptionPtr>(address);
+ SetThreadDescriptionFunc(::GetCurrentThread(),
+ L"Breakpad ExceptionHandler");
+ }
+ }
+
+ ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(lpParameter);
+ assert(self);
+ assert(self->handler_start_semaphore_ != NULL);
+ assert(self->handler_finish_semaphore_ != NULL);
+
+ for (;;) {
+ if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) ==
+ WAIT_OBJECT_0) {
+ // Perform the requested action.
+ if (self->is_shutdown_) {
+ // The instance of the exception handler is being destroyed.
+ break;
+ } else {
+ self->handler_return_value_ =
+ self->WriteMinidumpWithException(self->requesting_thread_id_,
+ self->exception_info_,
+ self->assertion_);
+ }
+
+ // Allow the requesting thread to proceed.
+ ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL);
+ }
+ }
+
+ // This statement is not reached when the thread is unconditionally
+ // terminated by the ExceptionHandler destructor.
+ return 0;
+}
+
+// HandleException and HandleInvalidParameter must create an
+// AutoExceptionHandler object to maintain static state and to determine which
+// ExceptionHandler instance to use. The constructor locates the correct
+// instance, and makes it available through get_handler(). The destructor
+// restores the state in effect prior to allocating the AutoExceptionHandler.
+class AutoExceptionHandler {
+ public:
+ AutoExceptionHandler() {
+ // Increment handler_stack_index_ so that if another Breakpad handler is
+ // registered using this same HandleException function, and it needs to be
+ // called while this handler is running (either because this handler
+ // declines to handle the exception, or an exception occurs during
+ // handling), HandleException will find the appropriate ExceptionHandler
+ // object in handler_stack_ to deliver the exception to.
+ //
+ // Because handler_stack_ is addressed in reverse (as |size - index|),
+ // preincrementing handler_stack_index_ avoids needing to subtract 1 from
+ // the argument to |at|.
+ //
+ // The index is maintained instead of popping elements off of the handler
+ // stack and pushing them at the end of this method. This avoids ruining
+ // the order of elements in the stack in the event that some other thread
+ // decides to manipulate the handler stack (such as creating a new
+ // ExceptionHandler object) while an exception is being handled.
+ EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
+ handler_ = ExceptionHandler::handler_stack_->at(
+ ExceptionHandler::handler_stack_->size() -
+ ++ExceptionHandler::handler_stack_index_);
+
+ // In case another exception occurs while this handler is doing its thing,
+ // it should be delivered to the previous filter.
+ SetUnhandledExceptionFilter(handler_->previous_filter_);
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ _set_invalid_parameter_handler(handler_->previous_iph_);
+#endif // _MSC_VER >= 1400
+ _set_purecall_handler(handler_->previous_pch_);
+ }
+
+ ~AutoExceptionHandler() {
+ // Put things back the way they were before entering this handler.
+ SetUnhandledExceptionFilter(ExceptionHandler::HandleException);
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter);
+#endif // _MSC_VER >= 1400
+ _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
+
+ --ExceptionHandler::handler_stack_index_;
+ LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
+ }
+
+ ExceptionHandler* get_handler() const { return handler_; }
+
+ private:
+ ExceptionHandler* handler_;
+};
+
+// static
+LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This
+ // logic will short-circuit before calling WriteMinidumpOnHandlerThread,
+ // allowing something else to handle the breakpoint without incurring the
+ // overhead transitioning to and from the handler thread. This behavior
+ // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions.
+ DWORD code = exinfo->ExceptionRecord->ExceptionCode;
+ LONG action;
+ bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
+ (code == EXCEPTION_SINGLE_STEP) ||
+ (code == DBG_PRINTEXCEPTION_C) ||
+ (code == DBG_PRINTEXCEPTION_WIDE_C);
+
+ if (code == EXCEPTION_INVALID_HANDLE &&
+ current_handler->consume_invalid_handle_exceptions_) {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
+ MinidumpResult result = MinidumpResult::Failure;
+
+ if (!is_debug_exception ||
+ current_handler->get_handle_debug_exceptions()) {
+ // If out-of-proc crash handler client is available, we have to use that
+ // to generate dump and we cannot fall back on in-proc dump generation
+ // because we never prepared for an in-proc dump generation
+
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (current_handler->IsOutOfProcess()) {
+ result = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ exinfo,
+ NULL);
+ } else {
+ result = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL)
+ ? MinidumpResult::Success : MinidumpResult::Failure;
+ }
+ }
+
+ // The handler fully handled the exception. Returning
+ // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually
+ // results in the application being terminated.
+ //
+ // Note: If the application was launched from within the Cygwin
+ // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the
+ // application to be restarted.
+ if ((result == MinidumpResult::Success) ||
+ (result == MinidumpResult::Ignored)) {
+ action = EXCEPTION_EXECUTE_HANDLER;
+ } else {
+ // There was an exception, it was a breakpoint or something else ignored
+ // above, or it was passed to the handler, which decided not to handle it.
+ // This could be because the filter callback didn't want it, because
+ // minidump writing failed for some reason, or because the post-minidump
+ // callback function indicated failure. Give the previous handler a
+ // chance to do something with the exception. If there is no previous
+ // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger
+ // or native "crashed" dialog to handle the exception.
+ if (current_handler->previous_filter_) {
+ action = current_handler->previous_filter_(exinfo);
+ } else {
+ action = EXCEPTION_CONTINUE_SEARCH;
+ }
+ }
+
+ return action;
+}
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+// static
+void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t reserved) {
+ // This is an invalid parameter, not an exception. It's safe to play with
+ // sprintf here.
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ MDRawAssertionInfo assertion;
+ memset(&assertion, 0, sizeof(assertion));
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression),
+ sizeof(assertion.expression) / sizeof(assertion.expression[0]),
+ _TRUNCATE, L"%s", expression);
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function),
+ sizeof(assertion.function) / sizeof(assertion.function[0]),
+ _TRUNCATE, L"%s", function);
+ _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.file),
+ sizeof(assertion.file) / sizeof(assertion.file[0]),
+ _TRUNCATE, L"%s", file);
+ assertion.line = line;
+ assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER;
+
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+
+ exception_record.ExceptionCode = STATUS_INVALID_PARAMETER;
+
+ // We store pointers to the the expression and function strings,
+ // and the line as exception parameters to make them easy to
+ // access by the developer on the far side.
+ exception_record.NumberParameters = 3;
+ exception_record.ExceptionInformation[0] =
+ reinterpret_cast<ULONG_PTR>(&assertion.expression);
+ exception_record.ExceptionInformation[1] =
+ reinterpret_cast<ULONG_PTR>(&assertion.file);
+ exception_record.ExceptionInformation[2] = assertion.line;
+
+ bool success = false;
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (current_handler->IsOutOfProcess()) {
+ success = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ &exception_ptrs,
+ &assertion) == MinidumpResult::Success;
+ } else {
+ success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
+ &assertion);
+ }
+
+ if (!success) {
+ if (current_handler->previous_iph_) {
+ // The handler didn't fully handle the exception. Give it to the
+ // previous invalid parameter handler.
+ current_handler->previous_iph_(expression,
+ function,
+ file,
+ line,
+ reserved);
+ } else {
+ // If there's no previous handler, pass the exception back in to the
+ // invalid parameter handler's core. That's the routine that called this
+ // function, but now, since this function is no longer registered (and in
+ // fact, no function at all is registered), this will result in the
+ // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson.
+ // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes
+ // more information through. In non-debug builds, it is not available,
+ // so fall back to using _invalid_parameter_noinfo. See invarg.c in the
+ // CRT source.
+#ifdef _DEBUG
+ _invalid_parameter(expression, function, file, line, reserved);
+#else // _DEBUG
+ _invalid_parameter_noinfo();
+#endif // _DEBUG
+ }
+ }
+
+ // The handler either took care of the invalid parameter problem itself,
+ // or passed it on to another handler. "Swallow" it by exiting, paralleling
+ // the behavior of "swallowing" exceptions.
+ exit(0);
+}
+#endif // _MSC_VER >= 1400
+
+// static
+void ExceptionHandler::HandlePureVirtualCall() {
+ // This is an pure virtual function call, not an exception. It's safe to
+ // play with sprintf here.
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ MDRawAssertionInfo assertion;
+ memset(&assertion, 0, sizeof(assertion));
+ assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
+
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+
+ exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+ // We store pointers to the the expression and function strings,
+ // and the line as exception parameters to make them easy to
+ // access by the developer on the far side.
+ exception_record.NumberParameters = 3;
+ exception_record.ExceptionInformation[0] =
+ reinterpret_cast<ULONG_PTR>(&assertion.expression);
+ exception_record.ExceptionInformation[1] =
+ reinterpret_cast<ULONG_PTR>(&assertion.file);
+ exception_record.ExceptionInformation[2] = assertion.line;
+
+ bool success = false;
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+
+ if (current_handler->IsOutOfProcess()) {
+ success = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(),
+ &exception_ptrs,
+ &assertion) == MinidumpResult::Success;
+ } else {
+ success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
+ &assertion);
+ }
+
+ if (!success) {
+ if (current_handler->previous_pch_) {
+ // The handler didn't fully handle the exception. Give it to the
+ // previous purecall handler.
+ current_handler->previous_pch_();
+ } else {
+ // If there's no previous handler, return and let _purecall handle it.
+ // This will just put up an assertion dialog.
+ return;
+ }
+ }
+
+ // The handler either took care of the invalid parameter problem itself,
+ // or passed it on to another handler. "Swallow" it by exiting, paralleling
+ // the behavior of "swallowing" exceptions.
+ exit(0);
+}
+
+// static
+LONG ExceptionHandler::HandleHeapCorruption(EXCEPTION_POINTERS* exinfo) {
+ if (exinfo->ExceptionRecord->ExceptionCode != STATUS_HEAP_CORRUPTION) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ AutoExceptionHandler auto_exception_handler;
+ ExceptionHandler* current_handler = auto_exception_handler.get_handler();
+
+ bool result = false;
+
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (current_handler->IsOutOfProcess()) {
+ result = current_handler->WriteMinidumpWithException(
+ GetCurrentThreadId(), exinfo, NULL) == MinidumpResult::Success;
+ } else {
+ result = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL);
+ }
+
+ return result ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+
+bool ExceptionHandler::WriteMinidumpOnHandlerThread(
+ EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) {
+ EnterCriticalSection(&handler_critical_section_);
+
+ // There isn't much we can do if the handler thread
+ // was not successfully created.
+ if (handler_thread_ == NULL) {
+ LeaveCriticalSection(&handler_critical_section_);
+ return false;
+ }
+
+ // The handler thread should only be created when the semaphores are valid.
+ assert(handler_start_semaphore_ != NULL);
+ assert(handler_finish_semaphore_ != NULL);
+
+ // Set up data to be passed in to the handler thread.
+ requesting_thread_id_ = GetCurrentThreadId();
+ exception_info_ = exinfo;
+ assertion_ = assertion;
+
+ // This causes the handler thread to call WriteMinidumpWithException.
+ ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
+
+ // Wait until WriteMinidumpWithException is done and collect its return value.
+ WaitForSingleObject(handler_finish_semaphore_, INFINITE);
+ bool status = (handler_return_value_ == MinidumpResult::Success);
+
+ // Clean up.
+ requesting_thread_id_ = 0;
+ exception_info_ = NULL;
+ assertion_ = NULL;
+
+ LeaveCriticalSection(&handler_critical_section_);
+
+ return status;
+}
+
+bool ExceptionHandler::WriteMinidump() {
+ // Make up an exception record for the current thread and CPU context
+ // to make it possible for the crash processor to classify these
+ // as do regular crashes, and to make it humane for developers to
+ // analyze them.
+ EXCEPTION_RECORD exception_record = {};
+ CONTEXT exception_context = {};
+ EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+ ::RtlCaptureContext(&exception_context);
+ exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+ return WriteMinidumpForException(&exception_ptrs);
+}
+
+bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) {
+ // In case of out-of-process dump generation, directly call
+ // WriteMinidumpWithException since there is no separate thread running.
+ if (IsOutOfProcess()) {
+ return WriteMinidumpWithException(GetCurrentThreadId(),
+ exinfo,
+ NULL) == MinidumpResult::Success;
+ }
+
+ bool success = WriteMinidumpOnHandlerThread(exinfo, NULL);
+ UpdateNextID();
+ return success;
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
+ MinidumpCallback callback,
+ void* callback_context,
+ MINIDUMP_TYPE dump_type) {
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context,
+ HANDLER_NONE, dump_type, (HANDLE)NULL, NULL);
+ return handler.WriteMinidump();
+}
+
+// static
+bool ExceptionHandler::WriteMinidumpForChild(HANDLE child,
+ DWORD child_blamed_thread,
+ const wstring& dump_path,
+ MinidumpCallback callback,
+ void* callback_context,
+ MINIDUMP_TYPE dump_type) {
+ EXCEPTION_RECORD ex;
+ CONTEXT ctx;
+ EXCEPTION_POINTERS exinfo = { NULL, NULL };
+ // As documented on MSDN, on failure SuspendThread returns (DWORD) -1
+ const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1);
+ DWORD last_suspend_count = kFailedToSuspendThread;
+ HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT |
+ THREAD_QUERY_INFORMATION |
+ THREAD_SUSPEND_RESUME,
+ FALSE,
+ child_blamed_thread);
+ // This thread may have died already, so not opening the handle is a
+ // non-fatal error.
+ if (child_thread_handle != NULL) {
+ last_suspend_count = SuspendThread(child_thread_handle);
+ if (last_suspend_count != kFailedToSuspendThread) {
+ ctx.ContextFlags = CONTEXT_ALL;
+ if (GetThreadContext(child_thread_handle, &ctx)) {
+ memset(&ex, 0, sizeof(ex));
+ ex.ExceptionCode = EXCEPTION_BREAKPOINT;
+#if defined(_M_IX86)
+ ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Eip);
+#elif defined(_M_X64)
+ ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Rip);
+#endif
+ exinfo.ExceptionRecord = &ex;
+ exinfo.ContextRecord = &ctx;
+ }
+ }
+ }
+
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context,
+ HANDLER_NONE, dump_type, (HANDLE)NULL, NULL);
+ bool success = handler.WriteMinidumpWithExceptionForProcess(
+ child_blamed_thread,
+ exinfo.ExceptionRecord ? &exinfo : NULL,
+ NULL, child, false);
+
+ if (last_suspend_count != kFailedToSuspendThread) {
+ ResumeThread(child_thread_handle);
+ }
+
+ CloseHandle(child_thread_handle);
+
+ if (callback) {
+ // nullptr here for phc::AddrInfo* is ok because this is not a crash.
+ success = callback(handler.dump_path_c_, handler.next_minidump_id_c_,
+ callback_context, NULL, NULL, nullptr, success);
+ }
+
+ return success;
+}
+
+#ifdef MOZ_PHC
+static void GetPHCAddrInfo(EXCEPTION_POINTERS* exinfo,
+ mozilla::phc::AddrInfo* addr_info) {
+ // Is this a crash involving a PHC allocation?
+ PEXCEPTION_RECORD rec = exinfo->ExceptionRecord;
+ if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
+ // rec->ExceptionInformation[0] contains a value indicating what type of
+ // operation it what, and rec->ExceptionInformation[1] contains the
+ // virtual address of the inaccessible data.
+ char* crashAddr = reinterpret_cast<char*>(rec->ExceptionInformation[1]);
+ mozilla::phc::IsPHCAllocation(crashAddr, addr_info);
+ }
+}
+#endif
+
+ExceptionHandler::MinidumpResult ExceptionHandler::WriteMinidumpWithException(
+ DWORD requesting_thread_id, EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion) {
+ mozilla::phc::AddrInfo* addr_info = nullptr;
+#ifdef MOZ_PHC
+ addr_info = &mozilla::phc::gAddrInfo;
+ GetPHCAddrInfo(exinfo, addr_info);
+#endif
+
+ // Give user code a chance to approve or prevent writing a minidump. If the
+ // filter returns Failure or Ignored , don't handle the exception and return
+ // the result for further handling downstream. If this method was called as a
+ // result of an exception, returning Failure will cause HandleException to
+ // call any previous handler or return EXCEPTION_CONTINUE_SEARCH on the
+ // exception thread, allowing it to appear as though this handler were not
+ // present at all.
+ if (filter_) {
+ switch (filter_(callback_context_, exinfo, assertion)) {
+ case FilterResult::HandleException: break;
+ case FilterResult::AbortWithoutMinidump: return MinidumpResult::Ignored;
+ case FilterResult::ContinueSearch: return MinidumpResult::Failure;
+ }
+ }
+
+ bool success = false;
+ if (IsOutOfProcess()) {
+ success = crash_generation_client_->RequestDump(exinfo, assertion);
+ } else {
+ success = WriteMinidumpWithExceptionForProcess(requesting_thread_id,
+ exinfo,
+ assertion,
+ GetCurrentProcess(),
+ true);
+ }
+
+ if (callback_) {
+ // TODO(munjal): In case of out-of-process dump generation, both
+ // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process
+ // scenario, the server process ends up creating the dump path and dump
+ // id so they are not known to the client.
+ success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
+ exinfo, assertion, addr_info, success);
+ }
+
+ return success ? MinidumpResult::Success : MinidumpResult::Failure;
+}
+
+// static
+bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
+ DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ HANDLE process,
+ bool write_requester_stream) {
+ bool success = false;
+ if (minidump_write_dump_) {
+ HANDLE dump_file = CreateFile(next_minidump_path_c_,
+ GENERIC_WRITE,
+ 0, // no sharing
+ NULL,
+ CREATE_NEW, // fail if exists
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (dump_file != INVALID_HANDLE_VALUE) {
+ MINIDUMP_EXCEPTION_INFORMATION except_info;
+ except_info.ThreadId = requesting_thread_id;
+ except_info.ExceptionPointers = exinfo;
+ except_info.ClientPointers = FALSE;
+
+ // Leave room in user_stream_array for possible breakpad and
+ // assertion info streams.
+ MINIDUMP_USER_STREAM user_stream_array[2];
+ MINIDUMP_USER_STREAM_INFORMATION user_streams;
+ user_streams.UserStreamCount = 0;
+ user_streams.UserStreamArray = user_stream_array;
+
+ if (write_requester_stream) {
+ // Add an MDRawBreakpadInfo stream to the minidump, to provide
+ // additional information about the exception handler to the Breakpad
+ // processor. The information will help the processor determine which
+ // threads are relevant. The Breakpad processor does not require this
+ // information but can function better with Breakpad-generated dumps
+ // when it is present. The native debugger is not harmed by the
+ // presence of this information.
+ MDRawBreakpadInfo breakpad_info;
+ breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
+ breakpad_info.dump_thread_id = GetCurrentThreadId();
+ breakpad_info.requesting_thread_id = requesting_thread_id;
+
+ int index = user_streams.UserStreamCount;
+ user_stream_array[index].Type = MD_BREAKPAD_INFO_STREAM;
+ user_stream_array[index].BufferSize = sizeof(breakpad_info);
+ user_stream_array[index].Buffer = &breakpad_info;
+ ++user_streams.UserStreamCount;
+ }
+
+ if (assertion) {
+ int index = user_streams.UserStreamCount;
+ user_stream_array[index].Type = MD_ASSERTION_INFO_STREAM;
+ user_stream_array[index].BufferSize = sizeof(MDRawAssertionInfo);
+ user_stream_array[index].Buffer = assertion;
+ ++user_streams.UserStreamCount;
+ }
+
+ MinidumpCallbackContext context;
+ context.iter = app_memory_info_.begin();
+ context.end = app_memory_info_.end();
+
+ if (exinfo) {
+ IncludeAppMemoryFromExceptionContext(process,
+ requesting_thread_id,
+ app_memory_info_,
+ exinfo->ContextRecord,
+ !include_context_heap_);
+ }
+
+ // Skip the reserved element if there was no instruction memory
+ if (context.iter->ptr == 0) {
+ context.iter++;
+ }
+
+ MINIDUMP_CALLBACK_INFORMATION callback;
+ callback.CallbackRoutine = MinidumpWriteDumpCallback;
+ callback.CallbackParam = reinterpret_cast<void*>(&context);
+
+ // The explicit comparison to TRUE avoids a warning (C4800).
+ success = (minidump_write_dump_(process,
+ GetProcessId(process),
+ dump_file,
+ dump_type_,
+ exinfo ? &except_info : NULL,
+ &user_streams,
+ &callback) == TRUE);
+
+ CloseHandle(dump_file);
+ }
+ }
+
+ return success;
+}
+
+void ExceptionHandler::UpdateNextID() {
+ assert(uuid_create_);
+ UUID id = {0};
+ if (uuid_create_) {
+ uuid_create_(&id);
+ }
+ next_minidump_id_ = GUIDString::GUIDToWString(&id);
+ next_minidump_id_c_ = next_minidump_id_.c_str();
+
+ wchar_t minidump_path[MAX_PATH];
+ swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp",
+ dump_path_c_, next_minidump_id_c_);
+
+ // remove when VC++7.1 is no longer supported
+ minidump_path[MAX_PATH - 1] = L'\0';
+
+ next_minidump_path_ = minidump_path;
+ next_minidump_path_c_ = next_minidump_path_.c_str();
+}
+
+void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr);
+ if (iter != app_memory_info_.end()) {
+ // Don't allow registering the same pointer twice.
+ return;
+ }
+
+ AppMemory app_memory;
+ app_memory.ptr = reinterpret_cast<ULONG64>(ptr);
+ app_memory.length = static_cast<ULONG>(length);
+ app_memory.preallocated = false;
+ app_memory_info_.push_back(app_memory);
+}
+
+void ExceptionHandler::UnregisterAppMemory(void* ptr) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr);
+ if (iter != app_memory_info_.end()) {
+ app_memory_info_.erase(iter);
+ }
+}
+
+void ExceptionHandler::set_include_context_heap(bool enabled) {
+ if (enabled && !include_context_heap_) {
+ // Initialize system info used in including context heap regions.
+ InitAppMemoryInternal();
+
+ // Preallocate AppMemory instances for exception context if necessary.
+ auto predicate = [] (const AppMemory& appMemory) -> bool {
+ return appMemory.preallocated;
+ };
+
+ int preallocatedCount =
+ std::count_if(app_memory_info_.begin(), app_memory_info_.end(), predicate);
+
+ for (size_t i = 0; i < kExceptionAppMemoryRegions - preallocatedCount; i++) {
+ AppMemory app_memory;
+ app_memory.ptr = reinterpret_cast<ULONG64>(nullptr);
+ app_memory.length = 0;
+ app_memory.preallocated = true;
+ app_memory_info_.push_back(app_memory);
+ }
+ }
+
+ include_context_heap_ = enabled;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.h b/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.h
new file mode 100644
index 0000000000..18c8f5821a
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/handler/exception_handler.h
@@ -0,0 +1,547 @@
+// 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.
+
+// ExceptionHandler can write a minidump file when an exception occurs,
+// or when WriteMinidump() is called explicitly by your program.
+//
+// To have the exception handler write minidumps when an uncaught exception
+// (crash) occurs, you should create an instance early in the execution
+// of your program, and keep it around for the entire time you want to
+// have crash handling active (typically, until shutdown).
+//
+// If you want to write minidumps without installing the exception handler,
+// you can create an ExceptionHandler with install_handler set to false,
+// then call WriteMinidump. You can also use this technique if you want to
+// use different minidump callbacks for different call sites.
+//
+// In either case, a callback function is called when a minidump is written,
+// which receives the unqiue id of the minidump. The caller can use this
+// id to collect and write additional application state, and to launch an
+// external crash-reporting application.
+//
+// It is important that creation and destruction of ExceptionHandler objects
+// be nested cleanly, when using install_handler = true.
+// Avoid the following pattern:
+// ExceptionHandler *e = new ExceptionHandler(...);
+// ExceptionHandler *f = new ExceptionHandler(...);
+// delete e;
+// This will put the exception filter stack into an inconsistent state.
+
+#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
+#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
+
+#include <stdlib.h>
+#include <windows.h>
+#include <dbghelp.h>
+#include <rpc.h>
+
+#pragma warning(push)
+// Disable exception handler warnings.
+#pragma warning(disable:4530)
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "windows/common/minidump_callback.h"
+#include "windows/common/ipc_protocol.h"
+#include "windows/crash_generation/crash_generation_client.h"
+#include "common/scoped_ptr.h"
+#include "google_breakpad/common/minidump_format.h"
+
+#ifdef MOZ_PHC
+#include "PHC.h"
+#else
+namespace mozilla { namespace phc { class AddrInfo {}; } }
+#endif
+
+namespace google_breakpad {
+
+using std::vector;
+using std::wstring;
+
+class ExceptionHandler {
+ public:
+ // The result value for the filter callback, see below.
+ enum class FilterResult {
+ HandleException,
+ AbortWithoutMinidump,
+ ContinueSearch
+ };
+
+ // A callback function to run before Breakpad performs any substantial
+ // processing of an exception. A FilterCallback is called before writing
+ // a minidump. context is the parameter supplied by the user as
+ // callback_context when the handler was created. exinfo points to the
+ // exception record, if any; assertion points to assertion information,
+ // if any.
+ //
+ // If a FilterCallback returns HandleException, Breakpad will attempt to
+ // write a minidump. If a FilterCallback returns ContinueSearch Breakpad
+ // will immediately report the exception as unhandled without writing a
+ // minidump, allowing another handler the opportunity to handle it.
+ // If a FilterCallback returns AbortWithoutMinidump, Breakpad will report the
+ // exception as handled but will not write a minidump, letting the process
+ // terminate itself instead.
+ typedef FilterResult (*FilterCallback)(void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion);
+
+ // A callback function to run after the minidump has been written.
+ // minidump_id is a unique id for the dump, so the minidump
+ // file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
+ // by the user as callback_context when the handler was created. exinfo
+ // points to the exception record, or NULL if no exception occurred.
+ // succeeded indicates whether a minidump file was successfully written.
+ // assertion points to information about an assertion if the handler was
+ // invoked by an assertion.
+ //
+ // If an exception occurred and the callback returns true, Breakpad will treat
+ // the exception as fully-handled, suppressing any other handlers from being
+ // notified of the exception. If the callback returns false, Breakpad will
+ // treat the exception as unhandled, and allow another handler to handle it.
+ // If there are no other handlers, Breakpad will report the exception to the
+ // system as unhandled, allowing a debugger or native crash dialog the
+ // opportunity to handle the exception. Most callback implementations
+ // should normally return the value of |succeeded|, or when they wish to
+ // not report an exception of handled, false. Callbacks will rarely want to
+ // return true directly (unless |succeeded| is true).
+ //
+ // For out-of-process dump generation, dump path and minidump ID will always
+ // be NULL. In case of out-of-process dump generation, the dump path and
+ // minidump id are controlled by the server process and are not communicated
+ // back to the crashing process.
+ typedef bool (*MinidumpCallback)(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ const mozilla::phc::AddrInfo* addr_info,
+ bool succeeded);
+
+ // HandlerType specifies which types of handlers should be installed, if
+ // any. Use HANDLER_NONE for an ExceptionHandler that remains idle,
+ // without catching any failures on its own. This type of handler may
+ // still be triggered by calling WriteMinidump. Otherwise, use a
+ // combination of the other HANDLER_ values, or HANDLER_ALL to install
+ // all handlers.
+ enum HandlerType {
+ HANDLER_NONE = 0,
+ HANDLER_EXCEPTION = 1 << 0, // SetUnhandledExceptionFilter
+ HANDLER_INVALID_PARAMETER = 1 << 1, // _set_invalid_parameter_handler
+ HANDLER_PURECALL = 1 << 2, // _set_purecall_handler
+ HANDLER_HEAP_CORRUPTION = 1 << 4, // AddVectoredExceptionHandler
+ HANDLER_ALL = HANDLER_EXCEPTION | HANDLER_INVALID_PARAMETER |
+ HANDLER_PURECALL | HANDLER_HEAP_CORRUPTION
+ };
+
+ // Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Before writing a minidump, the optional filter callback will be called.
+ // Its return value determines whether or not Breakpad should write a
+ // minidump. Minidump files will be written to dump_path, and the optional
+ // callback is called after writing the dump file, as described above.
+ // handler_types specifies the types of handlers that should be installed.
+ ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types);
+
+ // Creates a new ExceptionHandler instance that can attempt to perform
+ // out-of-process dump generation if pipe_name is not NULL. If pipe_name is
+ // NULL, or if out-of-process dump generation registration step fails,
+ // in-process dump generation will be used. This also allows specifying
+ // the dump type to generate.
+ ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ const CustomClientInfo* custom_info);
+
+ // As above, creates a new ExceptionHandler instance to perform
+ // out-of-process dump generation if the given pipe_handle is not NULL.
+ ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ HANDLE pipe_handle,
+ const CustomClientInfo* custom_info);
+
+ // ExceptionHandler that ENSURES out-of-process dump generation. Expects a
+ // crash generation client that is already registered with a crash generation
+ // server. Takes ownership of the passed-in crash_generation_client.
+ //
+ // Usage example:
+ // crash_generation_client = new CrashGenerationClient(..);
+ // if (crash_generation_client->Register()) {
+ // // Registration with the crash generation server succeeded.
+ // // Out-of-process dump generation is guaranteed.
+ // g_handler = new ExceptionHandler(.., crash_generation_client, ..);
+ // return true;
+ // }
+ ExceptionHandler(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ CrashGenerationClient* crash_generation_client);
+
+ ~ExceptionHandler();
+
+ // Get and set the minidump path.
+ wstring dump_path() const { return dump_path_; }
+ void set_dump_path(const wstring &dump_path) {
+ dump_path_ = dump_path;
+ dump_path_c_ = dump_path_.c_str();
+ UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_.
+ }
+
+ // Requests that a previously reported crash be uploaded.
+ bool RequestUpload(DWORD crash_id);
+
+ // Writes a minidump immediately. This can be used to capture the
+ // execution state independently of a crash. Returns true on success.
+ bool WriteMinidump();
+
+ // Writes a minidump immediately, with the user-supplied exception
+ // information.
+ bool WriteMinidumpForException(EXCEPTION_POINTERS* exinfo);
+
+ // Convenience form of WriteMinidump which does not require an
+ // ExceptionHandler instance.
+ static bool WriteMinidump(const wstring &dump_path,
+ MinidumpCallback callback, void* callback_context,
+ MINIDUMP_TYPE dump_type = MiniDumpNormal);
+
+ // Write a minidump of |child| immediately. This can be used to
+ // capture the execution state of |child| independently of a crash.
+ // Pass a meaningful |child_blamed_thread| to make that thread in
+ // the child process the one from which a crash signature is
+ // extracted.
+ static bool WriteMinidumpForChild(HANDLE child,
+ DWORD child_blamed_thread,
+ const wstring& dump_path,
+ MinidumpCallback callback,
+ void* callback_context,
+ MINIDUMP_TYPE dump_type = MiniDumpNormal);
+
+ // Get the thread ID of the thread requesting the dump (either the exception
+ // thread or any other thread that called WriteMinidump directly). This
+ // may be useful if you want to include additional thread state in your
+ // dumps.
+ DWORD get_requesting_thread_id() const { return requesting_thread_id_; }
+
+ // Controls behavior of EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP.
+ bool get_handle_debug_exceptions() const { return handle_debug_exceptions_; }
+ void set_handle_debug_exceptions(bool handle_debug_exceptions) {
+ handle_debug_exceptions_ = handle_debug_exceptions;
+ }
+
+ // Controls behavior of EXCEPTION_INVALID_HANDLE.
+ bool get_consume_invalid_handle_exceptions() const {
+ return consume_invalid_handle_exceptions_;
+ }
+ void set_consume_invalid_handle_exceptions(
+ bool consume_invalid_handle_exceptions) {
+ consume_invalid_handle_exceptions_ = consume_invalid_handle_exceptions;
+ }
+
+ // Returns whether out-of-process dump generation is used or not.
+ bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; }
+
+ // Calling RegisterAppMemory(p, len) causes len bytes starting
+ // at address p to be copied to the minidump when a crash happens.
+ void RegisterAppMemory(void* ptr, size_t length);
+ void UnregisterAppMemory(void* ptr);
+
+ // Calling set_include_context_heap(true) causes heap regions to be included
+ // in the minidump when a crash happens. The heap regions are from the
+ // register values of the crashing context.
+ void set_include_context_heap(bool enabled);
+
+ private:
+ friend class AutoExceptionHandler;
+
+ // Initializes the instance with given values.
+ void Initialize(const wstring& dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ int handler_types,
+ MINIDUMP_TYPE dump_type,
+ const wchar_t* pipe_name,
+ HANDLE pipe_handle,
+ CrashGenerationClient* crash_generation_client,
+ const CustomClientInfo* custom_info);
+
+ // Function pointer type for MiniDumpWriteDump, which is looked up
+ // dynamically.
+ typedef BOOL (WINAPI *MiniDumpWriteDump_type)(
+ HANDLE hProcess,
+ DWORD dwPid,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+
+ // Function pointer type for UuidCreate, which is looked up dynamically.
+ typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID* Uuid);
+
+ // Runs the main loop for the exception handler thread.
+ static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter);
+
+ // Called on the exception thread when an unhandled exception occurs.
+ // Signals the exception handler thread to handle the exception.
+ static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo);
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ // This function will be called by some CRT functions when they detect
+ // that they were passed an invalid parameter. Note that in _DEBUG builds,
+ // the CRT may display an assertion dialog before calling this function,
+ // and the function will not be called unless the assertion dialog is
+ // dismissed by clicking "Ignore."
+ static void HandleInvalidParameter(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t reserved);
+#endif // _MSC_VER >= 1400
+
+ // This function will be called by the CRT when a pure virtual
+ // function is called.
+ static void HandlePureVirtualCall();
+
+ // This function will be called by the vectored exception handler and will
+ // generate a minidump only for exceptions of type STATUS_HEAP_CORRUPTION.
+ static LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exinfo);
+
+ // This is called on the exception thread or on another thread that
+ // the user wishes to produce a dump from. It calls
+ // WriteMinidumpWithException on the handler thread, avoiding stack
+ // overflows and inconsistent dumps due to writing the dump from
+ // the exception thread. If the dump is requested as a result of an
+ // exception, exinfo contains exception information, otherwise, it
+ // is NULL. If the dump is requested as a result of an assertion
+ // (such as an invalid parameter being passed to a CRT function),
+ // assertion contains data about the assertion, otherwise, it is NULL.
+ bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion);
+
+ // The return value for WriteMinidumpWithException(), see below.
+ enum class MinidumpResult {
+ Success,
+ Failure,
+ Ignored
+ };
+
+ // This function is called on the handler thread. It calls into
+ // WriteMinidumpWithExceptionForProcess() with a handle to the
+ // current process. requesting_thread_id is the ID of the thread
+ // that requested the dump. If the dump is requested as a result of
+ // an exception, exinfo contains exception information, otherwise,
+ // it is NULL. It will return Success in case the minidump has been written
+ // successfully, Failure if we couldn't write out the minidump because of an
+ // error and Ignored if we didn't even try to write the minidump because the
+ // filter callback indicated that we should ignore this exception and abort.
+ // The latter condition is only relevent for a top-level exception handler,
+ // other callers should equate it to a failure.
+ MinidumpResult WriteMinidumpWithException(DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion);
+
+ // This function does the actual writing of a minidump. It is
+ // called on the handler thread. requesting_thread_id is the ID of
+ // the thread that requested the dump, if that information is
+ // meaningful. If the dump is requested as a result of an
+ // exception, exinfo contains exception information, otherwise, it
+ // is NULL. process is the one that will be dumped. If
+ // requesting_thread_id is meaningful and should be added to the
+ // minidump, write_requester_stream is |true|.
+ bool WriteMinidumpWithExceptionForProcess(DWORD requesting_thread_id,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ HANDLE process,
+ bool write_requester_stream);
+
+ // Generates a new ID and stores it in next_minidump_id_, and stores the
+ // path of the next minidump to be written in next_minidump_path_.
+ void UpdateNextID();
+
+ FilterCallback filter_;
+ MinidumpCallback callback_;
+ void* callback_context_;
+
+ scoped_ptr<CrashGenerationClient> crash_generation_client_;
+
+ // The directory in which a minidump will be written, set by the dump_path
+ // argument to the constructor, or set_dump_path.
+ wstring dump_path_;
+
+ // The basename of the next minidump to be written, without the extension.
+ wstring next_minidump_id_;
+
+ // The full pathname of the next minidump to be written, including the file
+ // extension.
+ wstring next_minidump_path_;
+
+ // Pointers to C-string representations of the above. These are set when
+ // the above wstring versions are set in order to avoid calling c_str during
+ // an exception, as c_str may attempt to allocate heap memory. These
+ // pointers are not owned by the ExceptionHandler object, but their lifetimes
+ // should be equivalent to the lifetimes of the associated wstring, provided
+ // that the wstrings are not altered.
+ const wchar_t* dump_path_c_;
+ const wchar_t* next_minidump_id_c_;
+ const wchar_t* next_minidump_path_c_;
+
+ HMODULE dbghelp_module_;
+ MiniDumpWriteDump_type minidump_write_dump_;
+ MINIDUMP_TYPE dump_type_;
+
+ HMODULE rpcrt4_module_;
+ UuidCreate_type uuid_create_;
+
+ // Tracks the handler types that were installed according to the
+ // handler_types constructor argument.
+ int handler_types_;
+
+ // When installed_handler_ is true, previous_filter_ is the unhandled
+ // exception filter that was set prior to installing ExceptionHandler as
+ // the unhandled exception filter and pointing it to |this|. NULL indicates
+ // that there is no previous unhandled exception filter.
+ LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_;
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ // Beginning in VC 8, the CRT provides an invalid parameter handler that will
+ // be called when some CRT functions are passed invalid parameters. In
+ // earlier CRTs, the same conditions would cause unexpected behavior or
+ // crashes.
+ _invalid_parameter_handler previous_iph_;
+#endif // _MSC_VER >= 1400
+
+ // The CRT allows you to override the default handler for pure
+ // virtual function calls.
+ _purecall_handler previous_pch_;
+
+ // Vectored exception handler used for catching STATUS_HEAP_CORRUPTION
+ // exceptions
+ PVOID heap_corruption_veh_;
+
+ // The exception handler thread.
+ HANDLE handler_thread_;
+
+ // True if the exception handler is being destroyed.
+ // Starting with MSVC 2005, Visual C has stronger guarantees on volatile vars.
+ // It has release semantics on write and acquire semantics on reads.
+ // See the msdn documentation.
+ volatile bool is_shutdown_;
+
+ // The critical section enforcing the requirement that only one exception be
+ // handled by a handler at a time.
+ CRITICAL_SECTION handler_critical_section_;
+
+ // Semaphores used to move exception handling between the exception thread
+ // and the handler thread. handler_start_semaphore_ is signalled by the
+ // exception thread to wake up the handler thread when an exception occurs.
+ // handler_finish_semaphore_ is signalled by the handler thread to wake up
+ // the exception thread when handling is complete.
+ HANDLE handler_start_semaphore_;
+ HANDLE handler_finish_semaphore_;
+
+ // The next 2 fields contain data passed from the requesting thread to
+ // the handler thread.
+
+ // The thread ID of the thread requesting the dump (either the exception
+ // thread or any other thread that called WriteMinidump directly).
+ DWORD requesting_thread_id_;
+
+ // The exception info passed to the exception handler on the exception
+ // thread, if an exception occurred. NULL for user-requested dumps.
+ EXCEPTION_POINTERS* exception_info_;
+
+ // If the handler is invoked due to an assertion, this will contain a
+ // pointer to the assertion information. It is NULL at other times.
+ MDRawAssertionInfo* assertion_;
+
+ // The return value of the handler, passed from the handler thread back to
+ // the requesting thread.
+ MinidumpResult handler_return_value_;
+
+ // If true, the handler will intercept EXCEPTION_BREAKPOINT and
+ // EXCEPTION_SINGLE_STEP exceptions. Leave this false (the default)
+ // to not interfere with debuggers.
+ bool handle_debug_exceptions_;
+
+ // If true, the handler will consume any EXCEPTION_INVALID_HANDLE exceptions.
+ // Leave this false (the default) to handle these exceptions as normal.
+ bool consume_invalid_handle_exceptions_;
+
+ // Callers can request additional memory regions to be included in
+ // the dump.
+ AppMemoryList app_memory_info_;
+
+ // A stack of ExceptionHandler objects that have installed unhandled
+ // exception filters. This vector is used by HandleException to determine
+ // which ExceptionHandler object to route an exception to. When an
+ // ExceptionHandler is created with install_handler true, it will append
+ // itself to this list.
+ static vector<ExceptionHandler*>* handler_stack_;
+
+ // The index of the ExceptionHandler in handler_stack_ that will handle the
+ // next exception. Note that 0 means the last entry in handler_stack_, 1
+ // means the next-to-last entry, and so on. This is used by HandleException
+ // to support multiple stacked Breakpad handlers.
+ static LONG handler_stack_index_;
+
+ // handler_stack_critical_section_ guards operations on handler_stack_ and
+ // handler_stack_index_. The critical section is initialized by the
+ // first instance of the class and destroyed by the last instance of it.
+ static CRITICAL_SECTION handler_stack_critical_section_;
+
+ // The number of instances of this class.
+ static volatile LONG instance_count_;
+
+ bool include_context_heap_;
+
+ // disallow copy ctor and operator=
+ explicit ExceptionHandler(const ExceptionHandler &);
+ void operator=(const ExceptionHandler &);
+};
+
+} // namespace google_breakpad
+
+#pragma warning(pop)
+
+#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/handler/objs.mozbuild b/toolkit/crashreporter/breakpad-client/windows/handler/objs.mozbuild
new file mode 100644
index 0000000000..22dc0d27dd
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/handler/objs.mozbuild
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+lobjs_handler = [
+ 'exception_handler.cc',
+]
+
+subdir = 'toolkit/crashreporter/breakpad-client/windows/handler'
+objs_handler = [
+ '/%s/%s' % (subdir, s) for s in lobjs_handler
+]
+
+if CONFIG['MOZ_PHC']:
+ DEFINES['MOZ_PHC'] = True
diff --git a/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.cc b/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.cc
new file mode 100644
index 0000000000..bf8ea5ea7a
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.cc
@@ -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.
+
+// Disable exception handler warnings.
+#pragma warning(disable : 4530)
+
+#include <errno.h>
+
+#include "windows/sender/crash_report_sender.h"
+#include "common/windows/http_upload.h"
+
+#if _MSC_VER < 1400 // MSVC 2005/8
+// Older MSVC doesn't have fscanf_s, but they are compatible as long as
+// we don't use the string conversions (%s/%c/%S/%C).
+# define fscanf_s fscanf
+#endif
+
+namespace google_breakpad {
+
+static const char kCheckpointSignature[] = "GBP1\n";
+
+CrashReportSender::CrashReportSender(const wstring& checkpoint_file)
+ : checkpoint_file_(checkpoint_file),
+ max_reports_per_day_(-1),
+ last_sent_date_(-1),
+ reports_sent_(0) {
+ FILE* fd;
+ if (OpenCheckpointFile(L"r", &fd) == 0) {
+ ReadCheckpoint(fd);
+ fclose(fd);
+ }
+}
+
+ReportResult CrashReportSender::SendCrashReport(
+ const wstring& url, const string& parameters,
+ const map<wstring, wstring>& files, wstring* report_code) {
+ int today = GetCurrentDate();
+ if (today == last_sent_date_ && max_reports_per_day_ != -1 &&
+ reports_sent_ >= max_reports_per_day_) {
+ return RESULT_THROTTLED;
+ }
+
+ int http_response = 0;
+ bool result = HTTPUpload::SendMultipartPostRequest(
+ url, parameters, files, NULL, report_code,
+ &http_response);
+
+ if (result) {
+ ReportSent(today);
+ return RESULT_SUCCEEDED;
+ } else if (http_response >= 400 && http_response < 500) {
+ return RESULT_REJECTED;
+ } else {
+ return RESULT_FAILED;
+ }
+}
+
+void CrashReportSender::ReadCheckpoint(FILE* fd) {
+ char buf[128];
+ if (!fgets(buf, sizeof(buf), fd) || strcmp(buf, kCheckpointSignature) != 0) {
+ return;
+ }
+
+ if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) {
+ last_sent_date_ = -1;
+ return;
+ }
+ if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) {
+ reports_sent_ = 0;
+ return;
+ }
+}
+
+void CrashReportSender::ReportSent(int today) {
+ // Update the report stats
+ if (today != last_sent_date_) {
+ last_sent_date_ = today;
+ reports_sent_ = 0;
+ }
+ ++reports_sent_;
+
+ // Update the checkpoint file
+ FILE* fd;
+ if (OpenCheckpointFile(L"w", &fd) == 0) {
+ fputs(kCheckpointSignature, fd);
+ fprintf(fd, "%d\n", last_sent_date_);
+ fprintf(fd, "%d\n", reports_sent_);
+ fclose(fd);
+ }
+}
+
+int CrashReportSender::GetCurrentDate() const {
+ SYSTEMTIME system_time;
+ GetSystemTime(&system_time);
+ return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
+ system_time.wDay;
+}
+
+int CrashReportSender::OpenCheckpointFile(const wchar_t* mode, FILE** fd) {
+ if (checkpoint_file_.empty()) {
+ return ENOENT;
+ }
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ return _wfopen_s(fd, checkpoint_file_.c_str(), mode);
+#else
+ *fd = _wfopen(checkpoint_file_.c_str(), mode);
+ if (*fd == NULL) {
+ return errno;
+ }
+ return 0;
+#endif
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.h b/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.h
new file mode 100644
index 0000000000..02b5f4fe49
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/sender/crash_report_sender.h
@@ -0,0 +1,122 @@
+// 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.
+
+#ifndef CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
+#define CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
+
+// CrashReportSender is a "static" class which provides an API to upload
+// crash reports via HTTP(S). A crash report is formatted as a multipart POST
+// request, which contains a set of caller-supplied string key/value pairs,
+// and a minidump file to upload.
+//
+// To use this library in your project, you will need to link against
+// wininet.lib.
+
+#pragma warning(push)
+// Disable exception handler warnings.
+#pragma warning(disable : 4530)
+
+#include <map>
+#include <string>
+
+namespace google_breakpad {
+
+using std::map;
+using std::string;
+using std::wstring;
+
+typedef enum {
+ RESULT_FAILED = 0, // Failed to communicate with the server; try later.
+ RESULT_REJECTED, // Successfully sent the crash report, but the
+ // server rejected it; don't resend this report.
+ RESULT_SUCCEEDED, // The server accepted the crash report.
+ RESULT_THROTTLED // No attempt was made to send the crash report, because
+ // we exceeded the maximum reports per day.
+} ReportResult;
+
+class CrashReportSender {
+ public:
+ // Initializes a CrashReportSender instance.
+ // If checkpoint_file is non-empty, breakpad will persist crash report
+ // state to this file. A checkpoint file is required for
+ // set_max_reports_per_day() to function properly.
+ explicit CrashReportSender(const wstring& checkpoint_file);
+ ~CrashReportSender() {}
+
+ // Sets the maximum number of crash reports that will be sent in a 24-hour
+ // period. This uses the state persisted to the checkpoint file.
+ // The default value of -1 means that there is no limit on reports sent.
+ void set_max_reports_per_day(int reports) { max_reports_per_day_ = reports; }
+
+ int max_reports_per_day() const { return max_reports_per_day_; }
+
+ // Sends the specified files, along with the map of
+ // name value pairs, as a multipart POST request to the given URL.
+ // Parameters are specified as a JSON-encoded string in |parameters|.
+ // Only HTTP(S) URLs are currently supported. The return value indicates
+ // the result of the operation (see above for possible results).
+ // If report_code is non-NULL and the report is sent successfully (that is,
+ // the return value is RESULT_SUCCEEDED), a code uniquely identifying the
+ // report will be returned in report_code.
+ // (Otherwise, report_code will be unchanged.)
+ ReportResult SendCrashReport(const wstring& url, const string& parameters,
+ const map<wstring, wstring>& files,
+ wstring* report_code);
+
+ private:
+ // Reads persistent state from a checkpoint file.
+ void ReadCheckpoint(FILE* fd);
+
+ // Called when a new report has been sent, to update the checkpoint state.
+ void ReportSent(int today);
+
+ // Returns today's date (UTC) formatted as YYYYMMDD.
+ int GetCurrentDate() const;
+
+ // Opens the checkpoint file with the specified mode.
+ // Returns zero on success, or an error code on failure.
+ int OpenCheckpointFile(const wchar_t* mode, FILE** fd);
+
+ wstring checkpoint_file_;
+ int max_reports_per_day_;
+ // The last date on which we sent a report, expressed as YYYYMMDD.
+ int last_sent_date_;
+ // Number of reports sent on last_sent_date_
+ int reports_sent_;
+
+ // Disallow copy constructor and operator=
+ explicit CrashReportSender(const CrashReportSender&);
+ void operator=(const CrashReportSender&);
+};
+
+} // namespace google_breakpad
+
+#pragma warning(pop)
+
+#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
diff --git a/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild b/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild
new file mode 100644
index 0000000000..3084bb92e6
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild
@@ -0,0 +1,14 @@
+# -*- 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/.
+
+lobjs_sender = [
+ 'crash_report_sender.cc',
+]
+
+subdir = 'toolkit/crashreporter/breakpad-client/windows/sender'
+objs_sender = [
+ '/%s/%s' % (subdir, s) for s in lobjs_sender
+]
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/crash_generation_server_test.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/crash_generation_server_test.cc
new file mode 100644
index 0000000000..09f2dd200d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/crash_generation_server_test.cc
@@ -0,0 +1,303 @@
+// 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.
+
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/crash_generation/crash_generation_server.h"
+#include "client/windows/common/ipc_protocol.h"
+
+using testing::_;
+
+namespace {
+
+const wchar_t kPipeName[] =
+ L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer";
+
+const DWORD kPipeDesiredAccess = FILE_READ_DATA |
+ FILE_WRITE_DATA |
+ FILE_WRITE_ATTRIBUTES;
+
+const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
+ SECURITY_SQOS_PRESENT;
+
+const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
+
+#define arraysize(f) (sizeof(f) / sizeof(*f))
+const google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
+ google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
+ google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
+};
+
+class CrashGenerationServerTest : public ::testing::Test {
+ public:
+ CrashGenerationServerTest()
+ : crash_generation_server_(kPipeName,
+ NULL,
+ CallOnClientConnected, &mock_callbacks_,
+ CallOnClientDumpRequested, &mock_callbacks_,
+ CallOnClientExited, &mock_callbacks_,
+ CallOnClientUploadRequested, &mock_callbacks_,
+ false,
+ NULL),
+ thread_id_(0),
+ exception_pointers_(NULL) {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ }
+
+ protected:
+ class MockCrashGenerationServerCallbacks {
+ public:
+ MOCK_METHOD1(OnClientConnected,
+ void(const google_breakpad::ClientInfo* client_info));
+ MOCK_METHOD2(OnClientDumpRequested,
+ void(const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path));
+ MOCK_METHOD1(OnClientExited,
+ void(const google_breakpad::ClientInfo* client_info));
+ MOCK_METHOD1(OnClientUploadRequested,
+ void(const DWORD crash_id));
+ };
+
+ enum ClientFault {
+ NO_FAULT,
+ CLOSE_AFTER_CONNECT,
+ SEND_INVALID_REGISTRATION,
+ TRUNCATE_REGISTRATION,
+ CLOSE_AFTER_REGISTRATION,
+ RESPONSE_BUFFER_TOO_SMALL,
+ CLOSE_AFTER_RESPONSE,
+ SEND_INVALID_ACK
+ };
+
+ void SetUp() {
+ ASSERT_TRUE(crash_generation_server_.Start());
+ }
+
+ void FaultyClient(ClientFault fault_type) {
+ HANDLE pipe = CreateFile(kPipeName,
+ kPipeDesiredAccess,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ kPipeFlagsAndAttributes,
+ NULL);
+
+ if (pipe == INVALID_HANDLE_VALUE) {
+ ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError());
+
+ // Cannot continue retrying if wait on pipe fails.
+ ASSERT_TRUE(WaitNamedPipe(kPipeName, 500));
+
+ pipe = CreateFile(kPipeName,
+ kPipeDesiredAccess,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ kPipeFlagsAndAttributes,
+ NULL);
+ }
+
+ ASSERT_NE(pipe, INVALID_HANDLE_VALUE);
+
+ DWORD mode = kPipeMode;
+ ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL));
+
+ DoFaultyClient(fault_type, pipe);
+
+ CloseHandle(pipe);
+ }
+
+ void DoTestFault(ClientFault fault) {
+ EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0);
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
+
+ EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
+
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
+
+ // Slight hack. The OnClientConnected is only invoked after the ack is
+ // received by the server. At that point, the FaultyClient call has already
+ // returned. The best way to wait until the server is done handling that is
+ // to send one more ping, whose processing will be blocked by delivery of
+ // the OnClientConnected message.
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
+ }
+
+ MockCrashGenerationServerCallbacks mock_callbacks_;
+
+ private:
+ // Depends on the caller to successfully open the pipe before invocation and
+ // to close it immediately afterwards.
+ void DoFaultyClient(ClientFault fault_type, HANDLE pipe) {
+ if (fault_type == CLOSE_AFTER_CONNECT) {
+ return;
+ }
+
+ google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
+ arraysize(kCustomInfoEntries)};
+
+ google_breakpad::ProtocolMessage msg(
+ fault_type == SEND_INVALID_REGISTRATION ?
+ google_breakpad::MESSAGE_TAG_NONE :
+ google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST,
+ GetCurrentProcessId(),
+ MiniDumpNormal,
+ &thread_id_,
+ &exception_pointers_,
+ &assert_info_,
+ custom_info,
+ NULL,
+ NULL,
+ NULL);
+
+ DWORD bytes_count = 0;
+
+ ASSERT_TRUE(WriteFile(pipe,
+ &msg,
+ fault_type == TRUNCATE_REGISTRATION ?
+ sizeof(msg) / 2 : sizeof(msg),
+ &bytes_count,
+ NULL));
+
+ if (fault_type == CLOSE_AFTER_REGISTRATION) {
+ return;
+ }
+
+ google_breakpad::ProtocolMessage reply;
+
+ if (!ReadFile(pipe,
+ &reply,
+ fault_type == RESPONSE_BUFFER_TOO_SMALL ?
+ sizeof(google_breakpad::ProtocolMessage) / 2 :
+ sizeof(google_breakpad::ProtocolMessage),
+ &bytes_count,
+ NULL)) {
+ switch (fault_type) {
+ case TRUNCATE_REGISTRATION:
+ case RESPONSE_BUFFER_TOO_SMALL:
+ case SEND_INVALID_REGISTRATION:
+ return;
+
+ default:
+ FAIL() << "Unexpectedly failed to register.";
+ }
+ }
+
+ if (fault_type == CLOSE_AFTER_RESPONSE) {
+ return;
+ }
+
+ google_breakpad::ProtocolMessage ack_msg;
+ ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK;
+
+ ASSERT_TRUE(WriteFile(pipe,
+ &ack_msg,
+ SEND_INVALID_ACK ?
+ sizeof(ack_msg) : sizeof(ack_msg) / 2,
+ &bytes_count,
+ NULL));
+
+ return;
+ }
+
+ static void CallOnClientConnected(
+ void* context, const google_breakpad::ClientInfo* client_info) {
+ static_cast<MockCrashGenerationServerCallbacks*>(context)->
+ OnClientConnected(client_info);
+ }
+
+ static void CallOnClientDumpRequested(
+ void* context,
+ const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path) {
+ static_cast<MockCrashGenerationServerCallbacks*>(context)->
+ OnClientDumpRequested(client_info, file_path);
+ }
+
+ static void CallOnClientExited(
+ void* context, const google_breakpad::ClientInfo* client_info) {
+ static_cast<MockCrashGenerationServerCallbacks*>(context)->
+ OnClientExited(client_info);
+ }
+
+ static void CallOnClientUploadRequested(void* context, const DWORD crash_id) {
+ static_cast<MockCrashGenerationServerCallbacks*>(context)->
+ OnClientUploadRequested(crash_id);
+ }
+
+ DWORD thread_id_;
+ EXCEPTION_POINTERS* exception_pointers_;
+ MDRawAssertionInfo assert_info_;
+
+ google_breakpad::CrashGenerationServer crash_generation_server_;
+};
+
+TEST_F(CrashGenerationServerTest, PingServerTest) {
+ DoTestFault(CLOSE_AFTER_CONNECT);
+}
+
+TEST_F(CrashGenerationServerTest, InvalidRegistration) {
+ DoTestFault(SEND_INVALID_REGISTRATION);
+}
+
+TEST_F(CrashGenerationServerTest, TruncateRegistration) {
+ DoTestFault(TRUNCATE_REGISTRATION);
+}
+
+TEST_F(CrashGenerationServerTest, CloseAfterRegistration) {
+ DoTestFault(CLOSE_AFTER_REGISTRATION);
+}
+
+TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) {
+ DoTestFault(RESPONSE_BUFFER_TOO_SMALL);
+}
+
+TEST_F(CrashGenerationServerTest, CloseAfterResponse) {
+ DoTestFault(CLOSE_AFTER_RESPONSE);
+}
+
+// It turns out that, as long as you send one byte, the ACK is accepted and
+// registration succeeds.
+TEST_F(CrashGenerationServerTest, SendInvalidAck) {
+ EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK));
+
+ // See DoTestFault for an explanation of this line
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
+
+ EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
+
+ // See DoTestFault for an explanation of this line
+ ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
+}
+
+} // anonymous namespace
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.cc
new file mode 100644
index 0000000000..53d4ddbde9
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.cc
@@ -0,0 +1,184 @@
+// 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 <windows.h>
+#include <objbase.h>
+#include <dbghelp.h>
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/unittests/dump_analysis.h" // NOLINT
+
+DumpAnalysis::~DumpAnalysis() {
+ if (dump_file_view_ != NULL) {
+ EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_));
+ ::CloseHandle(dump_file_mapping_);
+ dump_file_mapping_ = NULL;
+ }
+
+ if (dump_file_handle_ != NULL) {
+ ::CloseHandle(dump_file_handle_);
+ dump_file_handle_ = NULL;
+ }
+}
+
+void DumpAnalysis::EnsureDumpMapped() {
+ if (dump_file_view_ == NULL) {
+ dump_file_handle_ = ::CreateFile(dump_file_.c_str(),
+ GENERIC_READ,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ ASSERT_TRUE(dump_file_handle_ != NULL);
+ ASSERT_TRUE(dump_file_mapping_ == NULL);
+
+ dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ NULL);
+ ASSERT_TRUE(dump_file_mapping_ != NULL);
+
+ dump_file_view_ = ::MapViewOfFile(dump_file_mapping_,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0);
+ ASSERT_TRUE(dump_file_view_ != NULL);
+ }
+}
+
+bool DumpAnalysis::HasTebs() const {
+ MINIDUMP_THREAD_LIST* thread_list = NULL;
+ size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
+
+ if (thread_list_size > 0 && thread_list != NULL) {
+ for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) {
+ if (!HasMemory(thread_list->Threads[i].Teb))
+ return false;
+ }
+
+ return true;
+ }
+
+ // No thread list, no TEB info.
+ return false;
+}
+
+bool DumpAnalysis::HasPeb() const {
+ MINIDUMP_THREAD_LIST* thread_list = NULL;
+ size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
+
+ if (thread_list_size > 0 && thread_list != NULL &&
+ thread_list->NumberOfThreads > 0) {
+ FakeTEB* teb = NULL;
+ if (!HasMemory(thread_list->Threads[0].Teb, &teb))
+ return false;
+
+ return HasMemory(teb->peb);
+ }
+
+ return false;
+}
+
+bool DumpAnalysis::HasStream(ULONG stream_number) const {
+ void* stream = NULL;
+ size_t stream_size = GetStreamImpl(stream_number, &stream);
+ return stream_size > 0 && stream != NULL;
+}
+
+size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const {
+ MINIDUMP_DIRECTORY* directory = NULL;
+ ULONG memory_list_size = 0;
+ BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_,
+ stream_number,
+ &directory,
+ stream,
+ &memory_list_size);
+
+ return ret ? memory_list_size : 0;
+}
+
+bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize,
+ void **structure) const {
+ uintptr_t address = reinterpret_cast<uintptr_t>(addr_in);
+ MINIDUMP_MEMORY_LIST* memory_list = NULL;
+ size_t memory_list_size = GetStream(MemoryListStream, &memory_list);
+ if (memory_list_size > 0 && memory_list != NULL) {
+ for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) {
+ MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i];
+ const uintptr_t range_start =
+ static_cast<uintptr_t>(descr.StartOfMemoryRange);
+ uintptr_t range_end = range_start + descr.Memory.DataSize;
+
+ if (address >= range_start &&
+ address + structuresize < range_end) {
+ // The start address falls in the range, and the end address is
+ // in bounds, return a pointer to the structure if requested.
+ if (structure != NULL)
+ *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva);
+
+ return true;
+ }
+ }
+ }
+
+ // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this
+ // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the
+ // end of the dump file.
+ MINIDUMP_MEMORY64_LIST* memory64_list = NULL;
+ memory_list_size = GetStream(Memory64ListStream, &memory64_list);
+ if (memory_list_size > 0 && memory64_list != NULL) {
+ // Keep track of where the current descriptor maps to.
+ RVA64 curr_rva = memory64_list->BaseRva;
+ for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) {
+ MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i];
+ uintptr_t range_start =
+ static_cast<uintptr_t>(descr.StartOfMemoryRange);
+ uintptr_t range_end = range_start + static_cast<size_t>(descr.DataSize);
+
+ if (address >= range_start &&
+ address + structuresize < range_end) {
+ // The start address falls in the range, and the end address is
+ // in bounds, return a pointer to the structure if requested.
+ if (structure != NULL)
+ *structure = RVA_TO_ADDR(dump_file_view_, curr_rva);
+
+ return true;
+ }
+
+ // Advance the current RVA.
+ curr_rva += descr.DataSize;
+ }
+ }
+
+ return false;
+}
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.h b/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.h
new file mode 100644
index 0000000000..6cef48d814
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/dump_analysis.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
+#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
+
+#include "client/windows/crash_generation/minidump_generator.h"
+
+// Convenience to get to the PEB pointer in a TEB.
+struct FakeTEB {
+ char dummy[0x30];
+ void* peb;
+};
+
+class DumpAnalysis {
+ public:
+ explicit DumpAnalysis(const std::wstring& file_path)
+ : dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL),
+ dump_file_handle_(NULL) {
+ EnsureDumpMapped();
+ }
+ ~DumpAnalysis();
+
+ bool HasStream(ULONG stream_number) const;
+
+ // This is template to keep type safety in the front, but we end up casting
+ // to void** inside the implementation to pass the pointer to Win32. So
+ // casting here is considered safe.
+ template <class StreamType>
+ size_t GetStream(ULONG stream_number, StreamType** stream) const {
+ return GetStreamImpl(stream_number, reinterpret_cast<void**>(stream));
+ }
+
+ bool HasTebs() const;
+ bool HasPeb() const;
+ bool HasMemory(ULONG64 address) const {
+ return HasMemory<BYTE>(address, NULL);
+ }
+
+ bool HasMemory(const void* address) const {
+ return HasMemory<BYTE>(address, NULL);
+ }
+
+ template <class StructureType>
+ bool HasMemory(ULONG64 address, StructureType** structure = NULL) const {
+ // We can't cope with 64 bit addresses for now.
+ if (address > 0xFFFFFFFFUL)
+ return false;
+
+ return HasMemory(reinterpret_cast<void*>(address), structure);
+ }
+
+ template <class StructureType>
+ bool HasMemory(const void* addr_in, StructureType** structure = NULL) const {
+ return HasMemoryImpl(addr_in, sizeof(StructureType),
+ reinterpret_cast<void**>(structure));
+ }
+
+ protected:
+ void EnsureDumpMapped();
+
+ HANDLE dump_file_mapping_;
+ HANDLE dump_file_handle_;
+ void* dump_file_view_;
+ std::wstring dump_file_;
+
+ private:
+ // This is the implementation of GetStream<>.
+ size_t GetStreamImpl(ULONG stream_number, void** stream) const;
+
+ // This is the implementation of HasMemory<>.
+ bool HasMemoryImpl(const void* addr_in, size_t pointersize,
+ void** structure) const;
+};
+
+#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_death_test.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_death_test.cc
new file mode 100644
index 0000000000..5ef9e64d1d
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_death_test.cc
@@ -0,0 +1,587 @@
+// Copyright 2009, 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 <windows.h>
+#include <dbghelp.h>
+#include <strsafe.h>
+#include <objbase.h>
+#include <shellapi.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/crash_generation/crash_generation_server.h"
+#include "client/windows/handler/exception_handler.h"
+#include "client/windows/unittests/exception_handler_test.h"
+#include "common/windows/string_utils-inl.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace {
+
+using std::wstring;
+using namespace google_breakpad;
+
+const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
+const char kSuccessIndicator[] = "success";
+const char kFailureIndicator[] = "failure";
+
+// Utility function to test for a path's existence.
+BOOL DoesPathExist(const TCHAR *path_name);
+
+enum OutOfProcGuarantee {
+ OUT_OF_PROC_GUARANTEED,
+ OUT_OF_PROC_BEST_EFFORT,
+};
+
+class ExceptionHandlerDeathTest : public ::testing::Test {
+ protected:
+ // Member variable for each test that they can use
+ // for temporary storage.
+ TCHAR temp_path_[MAX_PATH];
+ // Actually constructs a temp path name.
+ virtual void SetUp();
+ // A helper method that tests can use to crash.
+ void DoCrashAccessViolation(const OutOfProcGuarantee out_of_proc_guarantee);
+ void DoCrashPureVirtualCall();
+};
+
+void ExceptionHandlerDeathTest::SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ TCHAR temp_path[MAX_PATH] = { '\0' };
+ TCHAR test_name_wide[MAX_PATH] = { '\0' };
+ // We want the temporary directory to be what the OS returns
+ // to us, + the test case name.
+ GetTempPath(MAX_PATH, temp_path);
+ // The test case name is exposed as a c-style string,
+ // convert it to a wchar_t string.
+ int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
+ static_cast<int>(strlen(test_info->name())),
+ test_name_wide,
+ MAX_PATH);
+ if (!dwRet) {
+ assert(false);
+ }
+ StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
+ CreateDirectory(temp_path_, NULL);
+}
+
+BOOL DoesPathExist(const TCHAR *path_name) {
+ DWORD flags = GetFileAttributes(path_name);
+ if (flags == INVALID_FILE_ATTRIBUTES) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool MinidumpWrittenCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded) {
+ if (succeeded && DoesPathExist(dump_path)) {
+ fprintf(stderr, kSuccessIndicator);
+ } else {
+ fprintf(stderr, kFailureIndicator);
+ }
+ // If we don't flush, the output doesn't get sent before
+ // this process dies.
+ fflush(stderr);
+ return succeeded;
+}
+
+TEST_F(ExceptionHandlerDeathTest, InProcTest) {
+ // For the in-proc test, we just need to instantiate an exception
+ // handler in in-proc mode, and crash. Since the entire test is
+ // reexecuted in the child process, we don't have to worry about
+ // the semantics of the exception handler being inherited/not
+ // inherited across CreateProcess().
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ scoped_ptr<google_breakpad::ExceptionHandler> exc(
+ new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL,
+ &MinidumpWrittenCallback,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL));
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ int *i = NULL;
+ ASSERT_DEATH((*i)++, kSuccessIndicator);
+}
+
+static bool gDumpCallbackCalled = false;
+
+void clientDumpCallback(void *dump_context,
+ const google_breakpad::ClientInfo *client_info,
+ const std::wstring *dump_path) {
+ gDumpCallbackCalled = true;
+}
+
+void ExceptionHandlerDeathTest::DoCrashAccessViolation(
+ const OutOfProcGuarantee out_of_proc_guarantee) {
+ scoped_ptr<google_breakpad::ExceptionHandler> exc;
+
+ if (out_of_proc_guarantee == OUT_OF_PROC_GUARANTEED) {
+ google_breakpad::CrashGenerationClient *client =
+ new google_breakpad::CrashGenerationClient(kPipeName,
+ MiniDumpNormal,
+ NULL); // custom_info
+ ASSERT_TRUE(client->Register());
+ exc.reset(new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL, // filter
+ NULL, // callback
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ client));
+ } else {
+ ASSERT_TRUE(out_of_proc_guarantee == OUT_OF_PROC_BEST_EFFORT);
+ exc.reset(new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL, // filter
+ NULL, // callback
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ MiniDumpNormal,
+ kPipeName,
+ NULL)); // custom_info
+ }
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ // Although this is executing in the child process of the death test,
+ // if it's not true we'll still get an error rather than the crash
+ // being expected.
+ ASSERT_TRUE(exc->IsOutOfProcess());
+ int *i = NULL;
+ printf("%d\n", (*i)++);
+}
+
+TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
+ // We can take advantage of a detail of google test here to save some
+ // complexity in testing: when you do a death test, it actually forks.
+ // So we can make the main test harness the crash generation server,
+ // and call ASSERT_DEATH on a NULL dereference, it to expecting test
+ // the out of process scenario, since it's happening in a different
+ // process! This is different from the above because, above, we pass
+ // a NULL pipe name, and we also don't start a crash generation server.
+
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ std::wstring dump_path(temp_path_);
+ google_breakpad::CrashGenerationServer server(
+ kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL,
+ NULL, true, &dump_path);
+
+ // This HAS to be EXPECT_, because when this test case is executed in the
+ // child process, the server registration will fail due to the named pipe
+ // being the same.
+ EXPECT_TRUE(server.Start());
+ gDumpCallbackCalled = false;
+ ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_BEST_EFFORT), "");
+ EXPECT_TRUE(gDumpCallbackCalled);
+}
+
+TEST_F(ExceptionHandlerDeathTest, OutOfProcGuaranteedTest) {
+ // This is similar to the previous test (OutOfProcTest). The only difference
+ // is that in this test, the crash generation client is created and registered
+ // with the crash generation server outside of the ExceptionHandler
+ // constructor which allows breakpad users to opt out of the default
+ // in-process dump generation when the registration with the crash generation
+ // server fails.
+
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ std::wstring dump_path(temp_path_);
+ google_breakpad::CrashGenerationServer server(
+ kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL,
+ NULL, true, &dump_path);
+
+ // This HAS to be EXPECT_, because when this test case is executed in the
+ // child process, the server registration will fail due to the named pipe
+ // being the same.
+ EXPECT_TRUE(server.Start());
+ gDumpCallbackCalled = false;
+ ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_GUARANTEED), "");
+ EXPECT_TRUE(gDumpCallbackCalled);
+}
+
+TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) {
+ using google_breakpad::ExceptionHandler;
+
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
+ ExceptionHandler::HANDLER_INVALID_PARAMETER);
+
+ // Disable the message box for assertions
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ // Call with a bad argument. The invalid parameter will be swallowed
+ // and a dump will be generated, the process will exit(0).
+ ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), "");
+}
+
+
+struct PureVirtualCallBase {
+ PureVirtualCallBase() {
+ // We have to reinterpret so the linker doesn't get confused because the
+ // method isn't defined.
+ reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
+ }
+ virtual ~PureVirtualCallBase() {}
+ virtual void PureFunction() const = 0;
+};
+struct PureVirtualCall : public PureVirtualCallBase {
+ PureVirtualCall() { PureFunction(); }
+ virtual void PureFunction() const {}
+};
+
+void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
+ PureVirtualCall instance;
+}
+
+TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
+ using google_breakpad::ExceptionHandler;
+
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
+ ExceptionHandler::HANDLER_PURECALL);
+
+ // Disable the message box for assertions
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ // Calls a pure virtual function.
+ EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
+}
+
+wstring find_minidump_in_directory(const wstring &directory) {
+ wstring search_path = directory + L"\\*";
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return wstring();
+
+ wstring filename;
+ do {
+ const wchar_t extension[] = L".dmp";
+ const size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
+ const size_t filename_length = wcslen(find_data.cFileName);
+ if (filename_length > extension_length &&
+ wcsncmp(extension,
+ find_data.cFileName + filename_length - extension_length,
+ extension_length) == 0) {
+ filename = directory + L"\\" + find_data.cFileName;
+ break;
+ }
+ } while (FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+ return filename;
+}
+
+#ifndef ADDRESS_SANITIZER
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ scoped_ptr<google_breakpad::ExceptionHandler> exc(
+ new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL,
+ NULL,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL));
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ // Get some executable memory.
+ const uint32_t kMemorySize = 256; // bytes
+ const int kOffset = kMemorySize / 2;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kMemorySize,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE));
+ ASSERT_TRUE(memory);
+
+ // Write some instructions that will crash. Put them
+ // in the middle of the block of memory, because the
+ // minidump should contain 128 bytes on either side of the
+ // instruction pointer.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers at least 128 bytes on either
+ // side of the instruction pointer from the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_LE(kMemorySize, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint64_t ip_offset = instruction_pointer - region->GetBase();
+ EXPECT_GE(region->GetSize() - kOffset, ip_offset);
+ EXPECT_LE(kOffset, ip_offset);
+
+ uint8_t prefix_bytes[kOffset];
+ uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes,
+ sizeof(prefix_bytes)));
+ EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions)));
+ EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes,
+ sizeof(suffix_bytes)));
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ scoped_ptr<google_breakpad::ExceptionHandler> exc(
+ new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL,
+ NULL,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL));
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ SYSTEM_INFO sSysInfo; // Useful information about the system
+ GetSystemInfo(&sSysInfo); // Initialize the structure.
+
+ const uint32_t kMemorySize = 256; // bytes
+ const DWORD kPageSize = sSysInfo.dwPageSize;
+ const int kOffset = 0;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ // Get some executable memory. Specifically, reserve two pages,
+ // but only commit the second.
+ char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kPageSize * 2,
+ MEM_RESERVE,
+ PAGE_NOACCESS));
+ ASSERT_TRUE(all_memory);
+ char* memory = all_memory + kPageSize;
+ ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+
+ // Write some instructions that will crash. Put them
+ // in the middle of the block of memory, because the
+ // minidump should contain 128 bytes on either side of the
+ // instruction pointer.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize / 2, region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes + kOffset,
+ instructions, sizeof(instructions)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ scoped_ptr<google_breakpad::ExceptionHandler> exc(
+ new google_breakpad::ExceptionHandler(
+ temp_path_,
+ NULL,
+ NULL,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL));
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ SYSTEM_INFO sSysInfo; // Useful information about the system
+ GetSystemInfo(&sSysInfo); // Initialize the structure.
+
+ const DWORD kPageSize = sSysInfo.dwPageSize;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ const int kOffset = kPageSize - sizeof(instructions);
+ // Get some executable memory. Specifically, reserve two pages,
+ // but only commit the first.
+ char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kPageSize * 2,
+ MEM_RESERVE,
+ PAGE_NOACCESS));
+ ASSERT_TRUE(memory);
+ ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+
+ // Write some instructions that will crash.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ uint64_t instruction_pointer;
+ ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ const size_t kPrefixSize = 128; // bytes
+ EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
+ const uint8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ uint8_t prefix_bytes[kPrefixSize];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)));
+ EXPECT_EQ(0, memcmp(bytes + kPrefixSize,
+ instructions, sizeof(instructions)));
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+#endif // !ADDRESS_SANITIZER
+
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_nesting_test.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_nesting_test.cc
new file mode 100644
index 0000000000..3ae1d7cd05
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_nesting_test.cc
@@ -0,0 +1,327 @@
+// Copyright 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.
+
+#include <windows.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/handler/exception_handler.h"
+#include "client/windows/unittests/exception_handler_test.h"
+
+namespace {
+
+const char kFoo[] = "foo";
+const char kBar[] = "bar";
+
+const char kStartOfLine[] = "^";
+const char kEndOfLine[] = "$";
+
+const char kFilterReturnsTrue[] = "filter_returns_true";
+const char kFilterReturnsFalse[] = "filter_returns_false";
+
+const char kCallbackReturnsTrue[] = "callback_returns_true";
+const char kCallbackReturnsFalse[] = "callback_returns_false";
+
+bool DoesPathExist(const wchar_t *path_name) {
+ DWORD flags = GetFileAttributes(path_name);
+ if (flags == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ return true;
+}
+
+// A callback function to run before Breakpad performs any substantial
+// processing of an exception. A FilterCallback is called before writing
+// a minidump. context is the parameter supplied by the user as
+// callback_context when the handler was created. exinfo points to the
+// exception record, if any; assertion points to assertion information,
+// if any.
+//
+// If a FilterCallback returns true, Breakpad will continue processing,
+// attempting to write a minidump. If a FilterCallback returns false,
+// Breakpad will immediately report the exception as unhandled without
+// writing a minidump, allowing another handler the opportunity to handle it.
+template <bool filter_return_value>
+bool CrashHandlerFilter(void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion) {
+ if (filter_return_value) {
+ fprintf(stderr, kFilterReturnsTrue);
+ } else {
+ fprintf(stderr, kFilterReturnsFalse);
+ }
+ fflush(stderr);
+
+ return filter_return_value;
+}
+
+// A callback function to run after the minidump has been written.
+// minidump_id is a unique id for the dump, so the minidump
+// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
+// by the user as callback_context when the handler was created. exinfo
+// points to the exception record, or NULL if no exception occurred.
+// succeeded indicates whether a minidump file was successfully written.
+// assertion points to information about an assertion if the handler was
+// invoked by an assertion.
+//
+// If an exception occurred and the callback returns true, Breakpad will treat
+// the exception as fully-handled, suppressing any other handlers from being
+// notified of the exception. If the callback returns false, Breakpad will
+// treat the exception as unhandled, and allow another handler to handle it.
+// If there are no other handlers, Breakpad will report the exception to the
+// system as unhandled, allowing a debugger or native crash dialog the
+// opportunity to handle the exception. Most callback implementations
+// should normally return the value of |succeeded|, or when they wish to
+// not report an exception of handled, false. Callbacks will rarely want to
+// return true directly (unless |succeeded| is true).
+//
+// For out-of-process dump generation, dump path and minidump ID will always
+// be NULL. In case of out-of-process dump generation, the dump path and
+// minidump id are controlled by the server process and are not communicated
+// back to the crashing process.
+template <bool callback_return_value>
+bool MinidumpWrittenCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded) {
+ bool rv = false;
+ if (callback_return_value &&
+ succeeded &&
+ DoesPathExist(dump_path)) {
+ rv = true;
+ fprintf(stderr, kCallbackReturnsTrue);
+ } else {
+ fprintf(stderr, kCallbackReturnsFalse);
+ }
+ fflush(stderr);
+
+ return rv;
+}
+
+
+void DoCrash(const char *message) {
+ if (message) {
+ fprintf(stderr, "%s", message);
+ fflush(stderr);
+ }
+ int *i = NULL;
+ (*i)++;
+
+ ASSERT_TRUE(false);
+}
+
+void InstallExceptionHandlerAndCrash(bool install_filter,
+ bool filter_return_value,
+ bool install_callback,
+ bool callback_return_value) {
+ wchar_t temp_path[MAX_PATH] = { '\0' };
+ GetTempPath(MAX_PATH, temp_path);
+
+ ASSERT_TRUE(DoesPathExist(temp_path));
+ google_breakpad::ExceptionHandler exc(
+ temp_path,
+ install_filter ?
+ (filter_return_value ?
+ &CrashHandlerFilter<true> :
+ &CrashHandlerFilter<false>) :
+ NULL,
+ install_callback ?
+ (callback_return_value ?
+ &MinidumpWrittenCallback<true> :
+ &MinidumpWrittenCallback<false>) :
+ NULL,
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ DoCrash(NULL);
+}
+
+TEST(AssertDeathSanity, Simple) {
+ ASSERT_DEATH(DoCrash(NULL), "");
+}
+
+TEST(AssertDeathSanity, Regex) {
+ ASSERT_DEATH(DoCrash(kFoo),
+ std::string(kStartOfLine) +
+ std::string(kFoo) +
+ std::string(kEndOfLine));
+
+ ASSERT_DEATH(DoCrash(kBar),
+ std::string(kStartOfLine) +
+ std::string(kBar) +
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ true, // filter_return_value
+ false, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsTrue) +
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ true, // filter_return_value
+ true, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsTrue) +
+ std::string(kCallbackReturnsFalse) +
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ false, // filter_return_value
+ false, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsFalse) +
+ std::string(kEndOfLine));
+}
+
+// Callback shouldn't be executed when filter returns false
+TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ false, // filter_return_value
+ true, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsFalse) +
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(false, // install_filter
+ true, // filter_return_value
+ false, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerCallbacks, No_Filter_Callback) {
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(false, // install_filter
+ true, // filter_return_value
+ true, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kCallbackReturnsFalse) +
+ std::string(kEndOfLine));
+}
+
+
+TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) {
+ wchar_t temp_path[MAX_PATH] = { '\0' };
+ GetTempPath(MAX_PATH, temp_path);
+
+ ASSERT_TRUE(DoesPathExist(temp_path));
+ google_breakpad::ExceptionHandler exc(
+ temp_path,
+ &CrashHandlerFilter<true>,
+ &MinidumpWrittenCallback<false>,
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
+
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ false, // filter_return_value
+ true, // install_callback
+ true), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsFalse) + // inner filter
+ std::string(kFilterReturnsTrue) + // outer filter
+ std::string(kCallbackReturnsFalse) + // outer callback
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) {
+ wchar_t temp_path[MAX_PATH] = { '\0' };
+ GetTempPath(MAX_PATH, temp_path);
+
+ ASSERT_TRUE(DoesPathExist(temp_path));
+ google_breakpad::ExceptionHandler exc(
+ temp_path,
+ &CrashHandlerFilter<true>,
+ &MinidumpWrittenCallback<false>,
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
+
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ true, // filter_return_value
+ true, // install_callback
+ false), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsTrue) + // inner filter
+ std::string(kCallbackReturnsFalse) + // inner callback
+ std::string(kFilterReturnsTrue) + // outer filter
+ std::string(kCallbackReturnsFalse) + // outer callback
+ std::string(kEndOfLine));
+}
+
+TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) {
+ wchar_t temp_path[MAX_PATH] = { '\0' };
+ GetTempPath(MAX_PATH, temp_path);
+
+ ASSERT_TRUE(DoesPathExist(temp_path));
+ google_breakpad::ExceptionHandler exc(
+ temp_path,
+ &CrashHandlerFilter<true>,
+ &MinidumpWrittenCallback<true>,
+ NULL, // callback_context
+ google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
+
+ ASSERT_DEATH(
+ InstallExceptionHandlerAndCrash(true, // install_filter
+ true, // filter_return_value
+ true, // install_callback
+ true), // callback_return_value
+ std::string(kStartOfLine) +
+ std::string(kFilterReturnsTrue) + // inner filter
+ std::string(kCallbackReturnsTrue) + // inner callback
+ std::string(kEndOfLine));
+}
+
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.cc
new file mode 100644
index 0000000000..a4ce12a8aa
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.cc
@@ -0,0 +1,503 @@
+// Copyright 2009, 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 "client/windows/unittests/exception_handler_test.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <strsafe.h>
+#include <objbase.h>
+#include <shellapi.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/crash_generation/crash_generation_server.h"
+#include "client/windows/handler/exception_handler.h"
+#include "client/windows/unittests/dump_analysis.h" // NOLINT
+#include "common/windows/string_utils-inl.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace testing {
+
+DisableExceptionHandlerInScope::DisableExceptionHandlerInScope() {
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ GTEST_FLAG(catch_exceptions) = false;
+}
+
+DisableExceptionHandlerInScope::~DisableExceptionHandlerInScope() {
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+}
+
+} // namespace testing
+
+namespace {
+
+using std::wstring;
+using namespace google_breakpad;
+
+const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
+const char kSuccessIndicator[] = "success";
+const char kFailureIndicator[] = "failure";
+
+const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithFullMemory | // Full memory from process.
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithHandleData); // Get all handle information.
+
+class ExceptionHandlerTest : public ::testing::Test {
+ protected:
+ // Member variable for each test that they can use
+ // for temporary storage.
+ TCHAR temp_path_[MAX_PATH];
+
+ // Actually constructs a temp path name.
+ virtual void SetUp();
+
+ // Deletes temporary files.
+ virtual void TearDown();
+
+ void DoCrashInvalidParameter();
+ void DoCrashPureVirtualCall();
+
+ // Utility function to test for a path's existence.
+ static BOOL DoesPathExist(const TCHAR *path_name);
+
+ // Client callback.
+ static void ClientDumpCallback(
+ void *dump_context,
+ const google_breakpad::ClientInfo *client_info,
+ const std::wstring *dump_path);
+
+ static bool DumpCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded);
+
+ static std::wstring dump_file;
+ static std::wstring full_dump_file;
+};
+
+std::wstring ExceptionHandlerTest::dump_file;
+std::wstring ExceptionHandlerTest::full_dump_file;
+
+void ExceptionHandlerTest::SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ TCHAR temp_path[MAX_PATH] = { '\0' };
+ TCHAR test_name_wide[MAX_PATH] = { '\0' };
+ // We want the temporary directory to be what the OS returns
+ // to us, + the test case name.
+ GetTempPath(MAX_PATH, temp_path);
+ // THe test case name is exposed to use as a c-style string,
+ // But we might be working in UNICODE here on Windows.
+ int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
+ static_cast<int>(strlen(test_info->name())),
+ test_name_wide,
+ MAX_PATH);
+ if (!dwRet) {
+ assert(false);
+ }
+ StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
+ CreateDirectory(temp_path_, NULL);
+}
+
+void ExceptionHandlerTest::TearDown() {
+ if (!dump_file.empty()) {
+ ::DeleteFile(dump_file.c_str());
+ dump_file = L"";
+ }
+ if (!full_dump_file.empty()) {
+ ::DeleteFile(full_dump_file.c_str());
+ full_dump_file = L"";
+ }
+}
+
+BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR *path_name) {
+ DWORD flags = GetFileAttributes(path_name);
+ if (flags == INVALID_FILE_ATTRIBUTES) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// static
+void ExceptionHandlerTest::ClientDumpCallback(
+ void *dump_context,
+ const google_breakpad::ClientInfo *client_info,
+ const wstring *dump_path) {
+ dump_file = *dump_path;
+ // Create the full dump file name from the dump path.
+ full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp";
+}
+
+// static
+bool ExceptionHandlerTest::DumpCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded) {
+ dump_file = dump_path;
+ dump_file += L"\\";
+ dump_file += minidump_id;
+ dump_file += L".dmp";
+ return succeeded;
+}
+
+void ExceptionHandlerTest::DoCrashInvalidParameter() {
+ google_breakpad::ExceptionHandler *exc =
+ new google_breakpad::ExceptionHandler(
+ temp_path_, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER,
+ kFullDumpType, kPipeName, NULL);
+
+ // Disable the message box for assertions
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ // Although this is executing in the child process of the death test,
+ // if it's not true we'll still get an error rather than the crash
+ // being expected.
+ ASSERT_TRUE(exc->IsOutOfProcess());
+ printf(NULL);
+}
+
+
+struct PureVirtualCallBase {
+ PureVirtualCallBase() {
+ // We have to reinterpret so the linker doesn't get confused because the
+ // method isn't defined.
+ reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
+ }
+ virtual ~PureVirtualCallBase() {}
+ virtual void PureFunction() const = 0;
+};
+struct PureVirtualCall : public PureVirtualCallBase {
+ PureVirtualCall() { PureFunction(); }
+ virtual void PureFunction() const {}
+};
+
+void ExceptionHandlerTest::DoCrashPureVirtualCall() {
+ google_breakpad::ExceptionHandler *exc =
+ new google_breakpad::ExceptionHandler(
+ temp_path_, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_PURECALL,
+ kFullDumpType, kPipeName, NULL);
+
+ // Disable the message box for assertions
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ // Although this is executing in the child process of the death test,
+ // if it's not true we'll still get an error rather than the crash
+ // being expected.
+ ASSERT_TRUE(exc->IsOutOfProcess());
+
+ // Create a new frame to ensure PureVirtualCall is not optimized to some
+ // other line in this function.
+ {
+ PureVirtualCall instance;
+ }
+}
+
+// This test validates that the minidump is written correctly.
+TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+
+ // Call with a bad argument
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ wstring dump_path(temp_path_);
+ google_breakpad::CrashGenerationServer server(
+ kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
+ NULL, true, &dump_path);
+
+ ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
+
+ // This HAS to be EXPECT_, because when this test case is executed in the
+ // child process, the server registration will fail due to the named pipe
+ // being the same.
+ EXPECT_TRUE(server.Start());
+ EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
+ ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
+ ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
+ ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
+
+ // Verify the dump for infos.
+ DumpAnalysis mini(dump_file);
+ DumpAnalysis full(full_dump_file);
+
+ // The dump should have all of these streams.
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(full.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(full.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(full.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(full.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+ EXPECT_TRUE(full.HasStream(MiscInfoStream));
+ EXPECT_TRUE(mini.HasStream(HandleDataStream));
+ EXPECT_TRUE(full.HasStream(HandleDataStream));
+
+ // We expect PEB and TEBs in this dump.
+ EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
+ EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
+
+ // Minidump should have a memory listing, but no 64-bit memory.
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+
+ EXPECT_FALSE(full.HasStream(MemoryListStream));
+ EXPECT_TRUE(full.HasStream(Memory64ListStream));
+
+ // This is the only place we don't use OR because we want both not
+ // to have the streams.
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(full.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(full.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(full.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(full.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(full.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+ EXPECT_FALSE(full.HasStream(TokenStream));
+}
+
+
+// This test validates that the minidump is written correctly.
+TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+
+ // Call with a bad argument
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ wstring dump_path(temp_path_);
+ google_breakpad::CrashGenerationServer server(
+ kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
+ NULL, true, &dump_path);
+
+ ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
+
+ // This HAS to be EXPECT_, because when this test case is executed in the
+ // child process, the server registration will fail due to the named pipe
+ // being the same.
+ EXPECT_TRUE(server.Start());
+ EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
+ ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
+ ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
+ ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
+
+ // Verify the dump for infos.
+ DumpAnalysis mini(dump_file);
+ DumpAnalysis full(full_dump_file);
+
+ // The dump should have all of these streams.
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(full.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(full.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(full.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(full.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+ EXPECT_TRUE(full.HasStream(MiscInfoStream));
+ EXPECT_TRUE(mini.HasStream(HandleDataStream));
+ EXPECT_TRUE(full.HasStream(HandleDataStream));
+
+ // We expect PEB and TEBs in this dump.
+ EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
+ EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
+
+ // Minidump should have a memory listing, but no 64-bit memory.
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+
+ EXPECT_FALSE(full.HasStream(MemoryListStream));
+ EXPECT_TRUE(full.HasStream(Memory64ListStream));
+
+ // This is the only place we don't use OR because we want both not
+ // to have the streams.
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(full.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(full.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(full.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(full.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(full.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+ EXPECT_FALSE(full.HasStream(TokenStream));
+}
+
+// Test that writing a minidump produces a valid minidump containing
+// some expected structures.
+TEST_F(ExceptionHandlerTest, WriteMinidumpTest) {
+ ExceptionHandler handler(temp_path_,
+ NULL,
+ DumpCallback,
+ NULL,
+ ExceptionHandler::HANDLER_ALL);
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ ASSERT_TRUE(handler.WriteMinidump());
+ ASSERT_FALSE(dump_file.empty());
+
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
+ &minidump_filename));
+
+ // Read the minidump and verify some info.
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+ // TODO(ted): more comprehensive tests...
+}
+
+// Test that an additional memory region can be included in the minidump.
+TEST_F(ExceptionHandlerTest, AdditionalMemory) {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ const uint32_t kMemorySize = si.dwPageSize;
+
+ // Get some heap memory.
+ uint8_t* memory = new uint8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (uint32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ ExceptionHandler handler(temp_path_,
+ NULL,
+ DumpCallback,
+ NULL,
+ ExceptionHandler::HANDLER_ALL);
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+ ASSERT_TRUE(handler.WriteMinidump());
+ ASSERT_FALSE(dump_file.empty());
+
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
+ &minidump_filename));
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemoryAddress, region->GetBase());
+ EXPECT_EQ(kMemorySize, region->GetSize());
+
+ // Verify memory contents.
+ EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
+
+ delete[] memory;
+}
+
+// Test that a memory region that was previously registered
+// can be unregistered.
+TEST_F(ExceptionHandlerTest, AdditionalMemoryRemove) {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ const uint32_t kMemorySize = si.dwPageSize;
+
+ // Get some heap memory.
+ uint8_t* memory = new uint8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (uint32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ ExceptionHandler handler(temp_path_,
+ NULL,
+ DumpCallback,
+ NULL,
+ ExceptionHandler::HANDLER_ALL);
+
+ // Disable GTest SEH handler
+ testing::DisableExceptionHandlerInScope disable_exception_handler;
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+
+ // ...and then remove it
+ handler.UnregisterAppMemory(memory);
+
+ ASSERT_TRUE(handler.WriteMinidump());
+ ASSERT_FALSE(dump_file.empty());
+
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
+ &minidump_filename));
+
+ // Read the minidump. Ensure that the memory region is not present.
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ EXPECT_FALSE(region);
+
+ delete[] memory;
+}
+
+} // namespace
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.h b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.h
new file mode 100644
index 0000000000..ef973e5392
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/exception_handler_test.h
@@ -0,0 +1,61 @@
+// Copyright 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.
+
+#ifndef CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_
+#define CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_
+
+namespace testing {
+
+// By default, GTest (on Windows) installs a SEH filter (and a handler) before
+// starting to run all the tests in order to avoid test interruptions is some
+// of the tests are crashing. Unfortunately, this functionality prevents the
+// execution to reach the UnhandledExceptionFilter installed by Google-Breakpad
+// ExceptionHandler so in order to test the Google-Breakpad exception handling
+// code the exception handling done by GTest must be disabled.
+// Usage:
+//
+// google_breakpad::ExceptionHandler exc(...);
+//
+// // Disable GTest SEH handler
+// testing::DisableExceptionHandlerInScope disable_exception_handler;
+// ...
+// ASSERT_DEATH( ... some crash ...);
+//
+class DisableExceptionHandlerInScope {
+ public:
+ DisableExceptionHandlerInScope();
+ ~DisableExceptionHandlerInScope();
+
+ private:
+ bool catch_exceptions_;
+};
+
+} // namespace testing
+
+#endif // CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_
diff --git a/toolkit/crashreporter/breakpad-client/windows/unittests/minidump_test.cc b/toolkit/crashreporter/breakpad-client/windows/unittests/minidump_test.cc
new file mode 100644
index 0000000000..82641125c1
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-client/windows/unittests/minidump_test.cc
@@ -0,0 +1,332 @@
+// 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 <windows.h>
+#include <objbase.h>
+#include <dbghelp.h>
+
+#include "breakpad_googletest_includes.h"
+#include "client/windows/crash_generation/minidump_generator.h"
+#include "client/windows/unittests/dump_analysis.h" // NOLINT
+
+namespace {
+
+// Minidump with stacks, PEB, TEB, and unloaded module list.
+const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules); // Get unloaded modules when available.
+
+// Minidump with all of the above, plus memory referenced from stack.
+const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules | // Get unloaded modules when available.
+ MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
+
+// Large dump with all process memory.
+const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithFullMemory | // Full memory from process.
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithHandleData | // Get all handle information.
+ MiniDumpWithUnloadedModules); // Get unloaded modules when available.
+
+class MinidumpTest: public testing::Test {
+ public:
+ MinidumpTest() {
+ wchar_t temp_dir_path[ MAX_PATH ] = {0};
+ ::GetTempPath(MAX_PATH, temp_dir_path);
+ dump_path_ = temp_dir_path;
+ }
+
+ virtual void SetUp() {
+ // Make sure URLMon isn't loaded into our process.
+ ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll"));
+
+ // Then load and unload it to ensure we have something to
+ // stock the unloaded module list with.
+ HMODULE urlmon = ::LoadLibrary(L"urlmon.dll");
+ ASSERT_TRUE(urlmon != NULL);
+ ASSERT_TRUE(::FreeLibrary(urlmon));
+ }
+
+ virtual void TearDown() {
+ if (!dump_file_.empty()) {
+ ::DeleteFile(dump_file_.c_str());
+ dump_file_ = L"";
+ }
+ if (!full_dump_file_.empty()) {
+ ::DeleteFile(full_dump_file_.c_str());
+ full_dump_file_ = L"";
+ }
+ }
+
+ bool WriteDump(ULONG flags) {
+ using google_breakpad::MinidumpGenerator;
+
+ // Fake exception is access violation on write to this.
+ EXCEPTION_RECORD ex_record = {
+ STATUS_ACCESS_VIOLATION, // ExceptionCode
+ 0, // ExceptionFlags
+ NULL, // ExceptionRecord;
+ reinterpret_cast<void*>(static_cast<uintptr_t>(0xCAFEBABE)), // ExceptionAddress;
+ 2, // NumberParameters;
+ { EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) }
+ };
+ CONTEXT ctx_record = {};
+ EXCEPTION_POINTERS ex_ptrs = {
+ &ex_record,
+ &ctx_record,
+ };
+
+ MinidumpGenerator generator(dump_path_,
+ ::GetCurrentProcess(),
+ ::GetCurrentProcessId(),
+ ::GetCurrentThreadId(),
+ ::GetCurrentThreadId(),
+ &ex_ptrs,
+ NULL,
+ static_cast<MINIDUMP_TYPE>(flags),
+ TRUE);
+ generator.GenerateDumpFile(&dump_file_);
+ generator.GenerateFullDumpFile(&full_dump_file_);
+ // And write a dump
+ bool result = generator.WriteMinidump();
+ return result == TRUE;
+ }
+
+ protected:
+ std::wstring dump_file_;
+ std::wstring full_dump_file_;
+
+ std::wstring dump_path_;
+};
+
+// We need to be able to get file information from Windows
+bool HasFileInfo(const std::wstring& file_path) {
+ DWORD dummy;
+ const wchar_t* path = file_path.c_str();
+ DWORD length = ::GetFileVersionInfoSize(path, &dummy);
+ if (length == 0)
+ return NULL;
+
+ void* data = calloc(length, 1);
+ if (!data)
+ return false;
+
+ if (!::GetFileVersionInfo(path, dummy, length, data)) {
+ free(data);
+ return false;
+ }
+
+ void* translate = NULL;
+ UINT page_count;
+ BOOL query_result = VerQueryValue(
+ data,
+ L"\\VarFileInfo\\Translation",
+ static_cast<void**>(&translate),
+ &page_count);
+
+ free(data);
+ if (query_result && translate) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+TEST_F(MinidumpTest, Version) {
+ // Loads DbgHelp.dll in process
+ ImagehlpApiVersion();
+
+ HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll");
+ ASSERT_TRUE(dbg_help != NULL);
+
+ wchar_t dbg_help_file[1024] = {};
+ ASSERT_TRUE(::GetModuleFileName(dbg_help,
+ dbg_help_file,
+ sizeof(dbg_help_file) /
+ sizeof(*dbg_help_file)));
+ ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL);
+
+// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version();
+}
+
+TEST_F(MinidumpTest, Normal) {
+ EXPECT_TRUE(WriteDump(MiniDumpNormal));
+ DumpAnalysis mini(dump_file_);
+
+ // We expect threads, modules and some memory.
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(HandleDataStream));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+
+ // We expect no PEB nor TEBs in this dump.
+ EXPECT_FALSE(mini.HasTebs());
+ EXPECT_FALSE(mini.HasPeb());
+
+ // We expect no off-stack memory in this dump.
+ EXPECT_FALSE(mini.HasMemory(this));
+}
+
+TEST_F(MinidumpTest, SmallDump) {
+ ASSERT_TRUE(WriteDump(kSmallDumpType));
+ DumpAnalysis mini(dump_file_);
+
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+
+ // We expect PEB and TEBs in this dump.
+ EXPECT_TRUE(mini.HasTebs());
+ EXPECT_TRUE(mini.HasPeb());
+
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(HandleDataStream));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+
+ // We expect no off-stack memory in this dump.
+ EXPECT_FALSE(mini.HasMemory(this));
+}
+
+TEST_F(MinidumpTest, LargerDump) {
+ ASSERT_TRUE(WriteDump(kLargerDumpType));
+ DumpAnalysis mini(dump_file_);
+
+ // The dump should have all of these streams.
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+
+ // We expect memory referenced by stack in this dump.
+ EXPECT_TRUE(mini.HasMemory(this));
+
+ // We expect PEB and TEBs in this dump.
+ EXPECT_TRUE(mini.HasTebs());
+ EXPECT_TRUE(mini.HasPeb());
+
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(HandleDataStream));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+}
+
+TEST_F(MinidumpTest, FullDump) {
+ ASSERT_TRUE(WriteDump(kFullDumpType));
+ ASSERT_TRUE(dump_file_ != L"");
+ ASSERT_TRUE(full_dump_file_ != L"");
+ DumpAnalysis mini(dump_file_);
+ DumpAnalysis full(full_dump_file_);
+
+ // Either dumps can contain part of the information.
+
+ // The dump should have all of these streams.
+ EXPECT_TRUE(mini.HasStream(ThreadListStream));
+ EXPECT_TRUE(full.HasStream(ThreadListStream));
+ EXPECT_TRUE(mini.HasStream(ModuleListStream));
+ EXPECT_TRUE(full.HasStream(ModuleListStream));
+ EXPECT_TRUE(mini.HasStream(ExceptionStream));
+ EXPECT_TRUE(full.HasStream(ExceptionStream));
+ EXPECT_TRUE(mini.HasStream(SystemInfoStream));
+ EXPECT_TRUE(full.HasStream(SystemInfoStream));
+ EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
+ EXPECT_TRUE(full.HasStream(UnloadedModuleListStream));
+ EXPECT_TRUE(mini.HasStream(MiscInfoStream));
+ EXPECT_TRUE(full.HasStream(MiscInfoStream));
+ EXPECT_TRUE(mini.HasStream(HandleDataStream));
+ EXPECT_TRUE(full.HasStream(HandleDataStream));
+
+ // We expect memory referenced by stack in this dump.
+ EXPECT_FALSE(mini.HasMemory(this));
+ EXPECT_TRUE(full.HasMemory(this));
+
+ // We expect PEB and TEBs in this dump.
+ EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
+ EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
+
+ EXPECT_TRUE(mini.HasStream(MemoryListStream));
+ EXPECT_TRUE(full.HasStream(Memory64ListStream));
+ EXPECT_FALSE(mini.HasStream(Memory64ListStream));
+ EXPECT_FALSE(full.HasStream(MemoryListStream));
+
+ // This is the only place we don't use OR because we want both not
+ // to have the streams.
+ EXPECT_FALSE(mini.HasStream(ThreadExListStream));
+ EXPECT_FALSE(full.HasStream(ThreadExListStream));
+ EXPECT_FALSE(mini.HasStream(CommentStreamA));
+ EXPECT_FALSE(full.HasStream(CommentStreamA));
+ EXPECT_FALSE(mini.HasStream(CommentStreamW));
+ EXPECT_FALSE(full.HasStream(CommentStreamW));
+ EXPECT_FALSE(mini.HasStream(FunctionTableStream));
+ EXPECT_FALSE(full.HasStream(FunctionTableStream));
+ EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
+ EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
+ EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(full.HasStream(HandleOperationListStream));
+ EXPECT_FALSE(mini.HasStream(TokenStream));
+ EXPECT_FALSE(full.HasStream(TokenStream));
+}
+
+} // namespace