summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/client')
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.h83
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.mm167
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.h162
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.mm362
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/InspectorMain.mm65
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/client_info.h47
-rw-r--r--3rdParty/Breakpad/src/client/mac/crash_generation/crash_generation_server.cc166
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/breakpad_nlist_64.cc17
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/dynamic_images.cc19
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/dynamic_images.h2
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc170
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/exception_handler.h8
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/mach_vm_compat.h7
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc324
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/minidump_generator.h36
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.cc92
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.h85
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/ucontext_compat.h47
-rw-r--r--3rdParty/Breakpad/src/client/minidump_file_writer.cc98
-rw-r--r--3rdParty/Breakpad/src/client/minidump_file_writer.h6
-rw-r--r--3rdParty/Breakpad/src/client/minidump_file_writer_unittest.cc179
-rw-r--r--3rdParty/Breakpad/src/client/windows/common/auto_critical_section.h81
-rw-r--r--3rdParty/Breakpad/src/client/windows/common/ipc_protocol.h4
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/ReadMe.txt58
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/client_info.cc223
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/client_info.h177
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc20
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.h2
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.cc943
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.h299
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.cc583
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.h203
-rw-r--r--3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc447
-rw-r--r--3rdParty/Breakpad/src/client/windows/handler/exception_handler.h107
34 files changed, 4915 insertions, 374 deletions
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.h b/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.h
new file mode 100644
index 0000000..5662e8b
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.h
@@ -0,0 +1,83 @@
+// 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.
+//
+// Utility class that can persist a SimpleStringDictionary to disk.
+
+#import <Foundation/Foundation.h>
+
+#include "common/simple_string_dictionary.h"
+
+namespace google_breakpad {
+
+BOOL EnsureDirectoryPathExists(NSString *dirPath);
+
+//=============================================================================
+class ConfigFile {
+ public:
+ ConfigFile() {
+ config_file_ = -1;
+ config_file_path_[0] = 0;
+ has_created_file_ = false;
+ };
+
+ ~ConfigFile() {
+ };
+
+ void WriteFile(const char* directory,
+ const SimpleStringDictionary *configurationParameters,
+ const char *dump_dir,
+ const char *minidump_id);
+
+ const char *GetFilePath() { return config_file_path_; }
+
+ void Unlink() {
+ if (config_file_ != -1)
+ unlink(config_file_path_);
+
+ config_file_ = -1;
+ }
+
+ private:
+ BOOL WriteData(const void *data, size_t length);
+
+ BOOL AppendConfigData(const char *key,
+ const void *data,
+ size_t length);
+
+ BOOL AppendConfigString(const char *key,
+ const char *value);
+
+ BOOL AppendCrashTimeParameters(const char *processStartTimeString);
+
+ int config_file_; // descriptor for config file
+ char config_file_path_[PATH_MAX]; // Path to configuration file
+ bool has_created_file_;
+};
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.mm b/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.mm
new file mode 100644
index 0000000..acab7de
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/ConfigFile.mm
@@ -0,0 +1,167 @@
+// 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.
+//
+// Utility class that can persist a SimpleStringDictionary to disk.
+
+#import "client/mac/crash_generation/ConfigFile.h"
+
+#import <Foundation/Foundation.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#import "client/apple/Framework/BreakpadDefines.h"
+#import "common/mac/GTMDefines.h"
+
+
+namespace google_breakpad {
+
+//=============================================================================
+BOOL EnsureDirectoryPathExists(NSString *dirPath) {
+ NSFileManager *mgr = [NSFileManager defaultManager];
+
+ NSDictionary *attrs =
+ [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
+ forKey:NSFilePosixPermissions];
+
+ return [mgr createDirectoryAtPath:dirPath
+ withIntermediateDirectories:YES
+ attributes:attrs
+ error:nil];
+}
+
+//=============================================================================
+BOOL ConfigFile::WriteData(const void *data, size_t length) {
+ size_t result = write(config_file_, data, length);
+
+ return result == length;
+}
+
+//=============================================================================
+BOOL ConfigFile::AppendConfigData(const char *key,
+ const void *data, size_t length) {
+ assert(config_file_ != -1);
+
+ if (!key) {
+ return NO;
+ }
+
+ if (!data) {
+ return NO;
+ }
+
+ // Write the key, \n, length of data (ascii integer), \n, data
+ char buffer[16];
+ char nl = '\n';
+ BOOL result = WriteData(key, strlen(key));
+
+ snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
+ result &= WriteData(buffer, strlen(buffer));
+ result &= WriteData(data, length);
+ result &= WriteData(&nl, 1);
+ return result;
+}
+
+//=============================================================================
+BOOL ConfigFile::AppendConfigString(const char *key,
+ const char *value) {
+ return AppendConfigData(key, value, strlen(value));
+}
+
+//=============================================================================
+BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) {
+ // Set process uptime parameter
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ char processUptimeString[32], processCrashtimeString[32];
+ // Set up time if we've received the start time.
+ if (processStartTimeString) {
+ time_t processStartTime = strtol(processStartTimeString, NULL, 10);
+ time_t processUptime = tv.tv_sec - processStartTime;
+ // Store the uptime in milliseconds.
+ sprintf(processUptimeString, "%llu",
+ static_cast<unsigned long long int>(processUptime) * 1000);
+ if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString))
+ return false;
+ }
+
+ sprintf(processCrashtimeString, "%zd", tv.tv_sec);
+ return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME,
+ processCrashtimeString);
+}
+
+//=============================================================================
+void ConfigFile::WriteFile(const char* directory,
+ const SimpleStringDictionary *configurationParameters,
+ const char *dump_dir,
+ const char *minidump_id) {
+
+ assert(config_file_ == -1);
+
+ // Open and write out configuration file preamble
+ if (directory) {
+ snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX",
+ directory);
+ } else {
+ strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
+ sizeof(config_file_path_));
+ }
+ config_file_ = mkstemp(config_file_path_);
+
+ if (config_file_ == -1) {
+ return;
+ }
+
+ has_created_file_ = true;
+
+ // Add the minidump dir
+ AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
+ AppendConfigString(kReporterMinidumpIDKey, minidump_id);
+
+ // Write out the configuration parameters
+ BOOL result = YES;
+ const SimpleStringDictionary &dictionary = *configurationParameters;
+
+ const SimpleStringDictionary::Entry *entry = NULL;
+ SimpleStringDictionary::Iterator iter(dictionary);
+
+ while ((entry = iter.Next())) {
+ result = AppendConfigString(entry->key, entry->value);
+
+ if (!result)
+ break;
+ }
+ AppendCrashTimeParameters(
+ configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME));
+
+ close(config_file_);
+ config_file_ = -1;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.h b/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.h
new file mode 100644
index 0000000..6712355
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Interface file between the Breakpad.framework and
+// the Inspector process.
+
+#include "common/simple_string_dictionary.h"
+
+#import <Foundation/Foundation.h>
+#include <mach/mach.h>
+
+#import "client/mac/crash_generation/ConfigFile.h"
+#import "client/mac/handler/minidump_generator.h"
+
+
+// Types of mach messsages (message IDs)
+enum {
+ kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo
+ kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData
+ kMsgType_InspectorAcknowledgement = 2 // no data sent
+};
+
+// Initial information sent from the crashed process by
+// Breakpad.framework to the Inspector process
+// The mach message with this struct as data will also include
+// several descriptors for sending mach port rights to the crashed
+// task, etc.
+struct InspectorInfo {
+ int exception_type;
+ int exception_code;
+ int exception_subcode;
+ unsigned int parameter_count; // key-value pairs
+};
+
+// Key/value message data to be sent to the Inspector
+struct KeyValueMessageData {
+ public:
+ KeyValueMessageData() {}
+ explicit KeyValueMessageData(
+ const google_breakpad::SimpleStringDictionary::Entry &inEntry) {
+ strlcpy(key, inEntry.key, sizeof(key) );
+ strlcpy(value, inEntry.value, sizeof(value) );
+ }
+
+ char key[google_breakpad::SimpleStringDictionary::key_size];
+ char value[google_breakpad::SimpleStringDictionary::value_size];
+};
+
+using std::string;
+using google_breakpad::MinidumpGenerator;
+
+namespace google_breakpad {
+
+//=============================================================================
+class MinidumpLocation {
+ public:
+ MinidumpLocation(NSString *minidumpDir) {
+ // Ensure that the path exists. Fallback to /tmp if unable to locate path.
+ assert(minidumpDir);
+ if (!EnsureDirectoryPathExists(minidumpDir)) {
+ minidumpDir = @"/tmp";
+ }
+
+ strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation],
+ sizeof(minidump_dir_path_));
+
+ // now generate a unique ID
+ string dump_path(minidump_dir_path_);
+ string next_minidump_id;
+
+ string next_minidump_path_ =
+ (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id));
+
+ strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_));
+ };
+
+ const char *GetPath() { return minidump_dir_path_; }
+ const char *GetID() { return minidump_id_; }
+
+ private:
+ char minidump_dir_path_[PATH_MAX]; // Path to minidump directory
+ char minidump_id_[128];
+};
+
+//=============================================================================
+class Inspector {
+ public:
+ Inspector() {};
+
+ // given a bootstrap service name, receives mach messages
+ // from a crashed process, then inspects it, creates a minidump file
+ // and asks the user if he wants to upload it to a server.
+ void Inspect(const char *receive_port_name);
+
+ private:
+ // The Inspector is invoked with its bootstrap port set to the bootstrap
+ // subset established in OnDemandServer.mm OnDemandServer::Initialize.
+ // For proper communication with the system, the sender (which will inherit
+ // the Inspector's bootstrap port) needs the per-session bootstrap namespace
+ // available directly in its bootstrap port. OnDemandServer stashed this
+ // port into the subset namespace under a special name. ResetBootstrapPort
+ // recovers this port and switches this task to use it as its own bootstrap
+ // (ensuring that children like the sender will inherit it), and saves the
+ // subset in bootstrap_subset_port_ for use by ServiceCheckIn and
+ // ServiceCheckOut.
+ kern_return_t ResetBootstrapPort();
+
+ kern_return_t ServiceCheckIn(const char *receive_port_name);
+ kern_return_t ServiceCheckOut(const char *receive_port_name);
+
+ kern_return_t ReadMessages();
+
+ bool InspectTask();
+ kern_return_t SendAcknowledgement();
+
+ // The bootstrap port in which the inspector is registered and into which it
+ // must check in.
+ mach_port_t bootstrap_subset_port_;
+
+ mach_port_t service_rcv_port_;
+
+ int exception_type_;
+ int exception_code_;
+ int exception_subcode_;
+ mach_port_t remote_task_;
+ mach_port_t crashing_thread_;
+ mach_port_t handler_thread_;
+ mach_port_t ack_port_;
+
+ SimpleStringDictionary config_params_;
+
+ ConfigFile config_file_;
+};
+
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.mm b/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.mm
new file mode 100644
index 0000000..dc6f480
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/Inspector.mm
@@ -0,0 +1,362 @@
+// 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.
+//
+// Utility that can inspect another process and write a crash dump
+
+#include <cstdio>
+#include <iostream>
+#include <servers/bootstrap.h>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+
+#import "client/mac/crash_generation/Inspector.h"
+
+#import "client/mac/Framework/Breakpad.h"
+#import "client/mac/handler/minidump_generator.h"
+
+#import "common/mac/MachIPC.h"
+#include "common/mac/bootstrap_compat.h"
+#include "common/mac/launch_reporter.h"
+
+#import "GTMDefines.h"
+
+#import <Foundation/Foundation.h>
+
+namespace google_breakpad {
+
+//=============================================================================
+void Inspector::Inspect(const char *receive_port_name) {
+ kern_return_t result = ResetBootstrapPort();
+ if (result != KERN_SUCCESS) {
+ return;
+ }
+
+ result = ServiceCheckIn(receive_port_name);
+
+ if (result == KERN_SUCCESS) {
+ result = ReadMessages();
+
+ if (result == KERN_SUCCESS) {
+ // Inspect the task and write a minidump file.
+ bool wrote_minidump = InspectTask();
+
+ // Send acknowledgement to the crashed process that the inspection
+ // has finished. It will then be able to cleanly exit.
+ // The return value is ignored because failure isn't fatal. If the process
+ // didn't get the message there's nothing we can do, and we still want to
+ // send the report.
+ SendAcknowledgement();
+
+ if (wrote_minidump) {
+ // Ask the user if he wants to upload the crash report to a server,
+ // and do so if he agrees.
+ LaunchReporter(
+ config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION),
+ config_file_.GetFilePath());
+ } else {
+ fprintf(stderr, "Inspection of crashed process failed\n");
+ }
+
+ // Now that we're done reading messages, cleanup the service, but only
+ // if there was an actual exception
+ // Otherwise, it means the dump was generated on demand and the process
+ // lives on, and we might be needed again in the future.
+ if (exception_code_) {
+ ServiceCheckOut(receive_port_name);
+ }
+ } else {
+ PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()");
+ }
+ }
+}
+
+//=============================================================================
+kern_return_t Inspector::ResetBootstrapPort() {
+ // A reasonable default, in case anything fails.
+ bootstrap_subset_port_ = bootstrap_port;
+
+ mach_port_t self_task = mach_task_self();
+
+ kern_return_t kr = task_get_bootstrap_port(self_task,
+ &bootstrap_subset_port_);
+ if (kr != KERN_SUCCESS) {
+ NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)",
+ mach_error_string(kr), kr);
+ return kr;
+ }
+
+ mach_port_t bootstrap_parent_port;
+ kr = bootstrap_look_up(bootstrap_subset_port_,
+ const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
+ &bootstrap_parent_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)",
+#if defined(MAC_OS_X_VERSION_10_5) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ bootstrap_strerror(kr),
+#else
+ mach_error_string(kr),
+#endif
+ kr);
+ return kr;
+ }
+
+ kr = task_set_bootstrap_port(self_task, bootstrap_parent_port);
+ if (kr != KERN_SUCCESS) {
+ NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)",
+ mach_error_string(kr), kr);
+ return kr;
+ }
+
+ // Some things access the bootstrap port through this global variable
+ // instead of calling task_get_bootstrap_port.
+ bootstrap_port = bootstrap_parent_port;
+
+ return KERN_SUCCESS;
+}
+
+//=============================================================================
+kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
+ // We need to get the mach port representing this service, so we can
+ // get information from the crashed process.
+ kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_,
+ (char*)receive_port_name,
+ &service_rcv_port_);
+
+ if (kr != KERN_SUCCESS) {
+#if VERBOSE
+ PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()");
+#endif
+ }
+
+ return kr;
+}
+
+//=============================================================================
+kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
+ // We're done receiving mach messages from the crashed process,
+ // so clean up a bit.
+ kern_return_t kr;
+
+ // DO NOT use mach_port_deallocate() here -- it will fail and the
+ // following bootstrap_register() will also fail leaving our service
+ // name hanging around forever (until reboot)
+ kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr,
+ "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()");
+ return kr;
+ }
+
+ // Unregister the service associated with the receive port.
+ kr = breakpad::BootstrapRegister(bootstrap_subset_port_,
+ (char*)receive_port_name,
+ MACH_PORT_NULL);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
+ }
+
+ return kr;
+}
+
+//=============================================================================
+kern_return_t Inspector::ReadMessages() {
+ // Wait for an initial message from the crashed process containing basic
+ // information about the crash.
+ ReceivePort receive_port(service_rcv_port_);
+
+ MachReceiveMessage message;
+ kern_return_t result = receive_port.WaitForMessage(&message, 1000);
+
+ if (result == KERN_SUCCESS) {
+ InspectorInfo &info = (InspectorInfo &)*message.GetData();
+ exception_type_ = info.exception_type;
+ exception_code_ = info.exception_code;
+ exception_subcode_ = info.exception_subcode;
+
+#if VERBOSE
+ printf("message ID = %d\n", message.GetMessageID());
+#endif
+
+ remote_task_ = message.GetTranslatedPort(0);
+ crashing_thread_ = message.GetTranslatedPort(1);
+ handler_thread_ = message.GetTranslatedPort(2);
+ ack_port_ = message.GetTranslatedPort(3);
+
+#if VERBOSE
+ printf("exception_type = %d\n", exception_type_);
+ printf("exception_code = %d\n", exception_code_);
+ printf("exception_subcode = %d\n", exception_subcode_);
+ printf("remote_task = %d\n", remote_task_);
+ printf("crashing_thread = %d\n", crashing_thread_);
+ printf("handler_thread = %d\n", handler_thread_);
+ printf("ack_port_ = %d\n", ack_port_);
+ printf("parameter count = %d\n", info.parameter_count);
+#endif
+
+ // In certain situations where multiple crash requests come
+ // through quickly, we can end up with the mach IPC messages not
+ // coming through correctly. Since we don't know what parameters
+ // we've missed, we can't do much besides abort the crash dump
+ // situation in this case.
+ unsigned int parameters_read = 0;
+ // The initial message contains the number of key value pairs that
+ // we are expected to read.
+ // Read each key/value pair, one mach message per key/value pair.
+ for (unsigned int i = 0; i < info.parameter_count; ++i) {
+ MachReceiveMessage parameter_message;
+ result = receive_port.WaitForMessage(&parameter_message, 1000);
+
+ if(result == KERN_SUCCESS) {
+ KeyValueMessageData &key_value_data =
+ (KeyValueMessageData&)*parameter_message.GetData();
+ // If we get a blank key, make sure we don't increment the
+ // parameter count; in some cases (notably on-demand generation
+ // many times in a short period of time) caused the Mach IPC
+ // messages to not come through correctly.
+ if (strlen(key_value_data.key) == 0) {
+ continue;
+ }
+ parameters_read++;
+
+ config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
+ } else {
+ PRINT_MACH_RESULT(result, "Inspector: key/value message");
+ break;
+ }
+ }
+ if (parameters_read != info.parameter_count) {
+ return KERN_FAILURE;
+ }
+ }
+
+ return result;
+}
+
+//=============================================================================
+bool Inspector::InspectTask() {
+ // keep the task quiet while we're looking at it
+ task_suspend(remote_task_);
+
+ NSString *minidumpDir;
+
+ const char *minidumpDirectory =
+ config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
+
+ // If the client app has not specified a minidump directory,
+ // use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
+ if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
+ NSArray *libraryDirectories =
+ NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
+ NSUserDomainMask,
+ YES);
+
+ NSString *applicationSupportDirectory =
+ [libraryDirectories objectAtIndex:0];
+ NSString *library_subdirectory = [NSString
+ stringWithUTF8String:kDefaultLibrarySubdirectory];
+ NSString *breakpad_product = [NSString
+ stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)];
+
+ NSArray *path_components = [NSArray
+ arrayWithObjects:applicationSupportDirectory,
+ library_subdirectory,
+ breakpad_product,
+ nil];
+
+ minidumpDir = [NSString pathWithComponents:path_components];
+ } else {
+ minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory]
+ stringByExpandingTildeInPath];
+ }
+
+ MinidumpLocation minidumpLocation(minidumpDir);
+
+ // Obscure bug alert:
+ // Don't use [NSString stringWithFormat] to build up the path here since it
+ // assumes system encoding and in RTL locales will prepend an LTR override
+ // character for paths beginning with '/' which fileSystemRepresentation does
+ // not remove. Filed as rdar://6889706 .
+ NSString *path_ns = [NSString
+ stringWithUTF8String:minidumpLocation.GetPath()];
+ NSString *pathid_ns = [NSString
+ stringWithUTF8String:minidumpLocation.GetID()];
+ NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns];
+ minidumpPath = [minidumpPath
+ stringByAppendingPathExtension:@"dmp"];
+
+ config_file_.WriteFile( 0,
+ &config_params_,
+ minidumpLocation.GetPath(),
+ minidumpLocation.GetID());
+
+
+ MinidumpGenerator generator(remote_task_, handler_thread_);
+
+ if (exception_type_ && exception_code_) {
+ generator.SetExceptionInformation(exception_type_,
+ exception_code_,
+ exception_subcode_,
+ crashing_thread_);
+ }
+
+
+ bool result = generator.Write([minidumpPath fileSystemRepresentation]);
+
+ // let the task continue
+ task_resume(remote_task_);
+
+ return result;
+}
+
+//=============================================================================
+// The crashed task needs to be told that the inspection has finished.
+// It will wait on a mach port (with timeout) until we send acknowledgement.
+kern_return_t Inspector::SendAcknowledgement() {
+ if (ack_port_ != MACH_PORT_DEAD) {
+ MachPortSender sender(ack_port_);
+ MachSendMessage ack_message(kMsgType_InspectorAcknowledgement);
+
+ kern_return_t result = sender.SendMessage(ack_message, 2000);
+
+#if VERBOSE
+ PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement");
+#endif
+
+ return result;
+ }
+
+ return KERN_INVALID_NAME;
+}
+
+} // namespace google_breakpad
+
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/InspectorMain.mm b/3rdParty/Breakpad/src/client/mac/crash_generation/InspectorMain.mm
new file mode 100644
index 0000000..137c6a1
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/InspectorMain.mm
@@ -0,0 +1,65 @@
+// 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.
+//
+// Main driver for Inspector
+
+#import "client/mac/crash_generation/Inspector.h"
+#import <Cocoa/Cocoa.h>
+
+namespace google_breakpad {
+
+//=============================================================================
+extern "C" {
+
+int main(int argc, char *const argv[]) {
+#if DEBUG
+ // Since we're launched on-demand, this is necessary to see debugging
+ // output in the console window.
+ freopen("/dev/console", "w", stdout);
+ freopen("/dev/console", "w", stderr);
+#endif
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (argc != 2) {
+ exit(0);
+ }
+ // Our first command-line argument contains the name of the service
+ // that we're providing.
+ google_breakpad::Inspector inspector;
+ inspector.Inspect(argv[1]);
+
+ [pool release];
+
+ return 0;
+}
+
+} // extern "C"
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/client_info.h b/3rdParty/Breakpad/src/client/mac/crash_generation/client_info.h
new file mode 100644
index 0000000..a3a95dc
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/client_info.h
@@ -0,0 +1,47 @@
+// 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:
+ explicit ClientInfo(pid_t pid) : pid_(pid) {}
+
+ pid_t pid() const { return pid_; }
+
+ private:
+ pid_t pid_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
diff --git a/3rdParty/Breakpad/src/client/mac/crash_generation/crash_generation_server.cc b/3rdParty/Breakpad/src/client/mac/crash_generation/crash_generation_server.cc
new file mode 100644
index 0000000..451e8d9
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/crash_generation/crash_generation_server.cc
@@ -0,0 +1,166 @@
+// 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 "client/mac/crash_generation/crash_generation_server.h"
+
+#include <pthread.h>
+
+#include "client/mac/crash_generation/client_info.h"
+#include "client/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) {
+ 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);
+ pid_t remote_pid = -1;
+ pid_for_task(remote_task, &remote_pid);
+ ClientInfo client(remote_pid);
+
+ 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);
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
+
+ sender.SendMessage(ack_message, kSendTimeoutMs);
+ }
+
+ if (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/3rdParty/Breakpad/src/client/mac/handler/breakpad_nlist_64.cc b/3rdParty/Breakpad/src/client/mac/handler/breakpad_nlist_64.cc
index b50aa03..3492b82 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/breakpad_nlist_64.cc
+++ b/3rdParty/Breakpad/src/client/mac/handler/breakpad_nlist_64.cc
@@ -202,17 +202,6 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC ||
/* The following is the big-endian ppc64 check */
*((unsigned int *)&buf) == FAT_MAGIC) {
- /* Get host info */
- host_t host = mach_host_self();
- unsigned i = HOST_BASIC_INFO_COUNT;
- struct host_basic_info hbi;
- kern_return_t kr;
- if ((kr = host_info(host, HOST_BASIC_INFO,
- (host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
- return -1;
- }
- mach_port_deallocate(mach_task_self(), host);
-
/* Read in the fat header */
struct fat_header fh;
if (lseek(fd, 0, SEEK_SET) == -1) {
@@ -281,7 +270,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
off_t sa; /* symbol address */
off_t ss; /* start of strings */
- register register_t n;
+ register_t n;
if (*((unsigned int *)&buf) == magic) {
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
@@ -354,14 +343,14 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
// and look for a match
while (n) {
nlist_type space[BUFSIZ/sizeof (nlist_type)];
- register register_t m = sizeof (space);
+ register_t m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
n -= m;
- long savpos = lseek(fd, 0, SEEK_CUR);
+ off_t savpos = lseek(fd, 0, SEEK_CUR);
if (savpos == -1) {
return -1;
}
diff --git a/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.cc b/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.cc
index ef5743c..cdba6df 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.cc
+++ b/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.cc
@@ -41,6 +41,7 @@ extern "C" { // needed to compile on Leopard
#include <mach/task_info.h>
#include <sys/sysctl.h>
#include <TargetConditionals.h>
+#include <unistd.h>
#include <algorithm>
#include <string>
@@ -261,8 +262,8 @@ bool FindTextSection(DynamicImage& image) {
reinterpret_cast<const mach_segment_command_type *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
- image.vmaddr_ = seg->vmaddr;
- image.vmsize_ = seg->vmsize;
+ image.vmaddr_ = static_cast<mach_vm_address_t>(seg->vmaddr);
+ image.vmsize_ = static_cast<mach_vm_size_t>(seg->vmsize);
image.slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
@@ -363,7 +364,7 @@ static uint64_t LookupSymbol(const char* symbol_name,
return list.n_value;
}
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
static bool HasTaskDyldInfo() {
return true;
}
@@ -380,13 +381,9 @@ static SInt32 GetOSVersion() {
}
static bool HasTaskDyldInfo() {
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
- return true;
-#else
return GetOSVersion() >= 0x1060;
-#endif
}
-#endif // TARGET_OS_IPHONE
+#endif // TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= 10_6
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
if (HasTaskDyldInfo()) {
@@ -475,8 +472,6 @@ void ReadImageInfo(DynamicImages& images,
mach_header_bytes) != KERN_SUCCESS)
continue;
- header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
-
// Read the file name from the task's memory space.
string file_path;
if (info.file_path_) {
@@ -492,7 +487,7 @@ void ReadImageInfo(DynamicImages& images,
header_size,
info.load_address_,
file_path,
- info.file_mod_date_,
+ static_cast<uintptr_t>(info.file_mod_date_),
images.task_,
images.cpu_type_);
@@ -568,7 +563,7 @@ cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
cpu_type_t cpu_type;
size_t cpuTypeSize = sizeof(cpu_type);
- sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
+ sysctl(mib, static_cast<u_int>(mibLen), &cpu_type, &cpuTypeSize, 0, 0);
return cpu_type;
}
diff --git a/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.h b/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.h
index d039eda..6514790 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.h
+++ b/3rdParty/Breakpad/src/client/mac/handler/dynamic_images.h
@@ -285,6 +285,8 @@ class DynamicImages {
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
diff --git a/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc
index 4043019..2a19d46 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc
+++ b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc
@@ -27,19 +27,32 @@
// (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 <map>
#include <mach/exc.h>
#include <mach/mig.h>
#include <pthread.h>
#include <signal.h>
#include <TargetConditionals.h>
+#include <map>
+
#include "client/mac/handler/exception_handler.h"
#include "client/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"
+#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
@@ -61,8 +74,12 @@ 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
+#endif // defined PAGE_MAX_SIZE
+#endif // USE_PROTECTED_ALLOCATIONS
google_breakpad::ExceptionHandler *handler;
} gProtectedData;
@@ -103,14 +120,13 @@ exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
#if !TARGET_OS_IPHONE
-extern "C"
-{
+extern "C" {
// Forward declarations for functions that need "C" style compilation
- boolean_t exc_server(mach_msg_header_t *request,
- mach_msg_header_t *reply);
+ boolean_t exc_server(mach_msg_header_t* request,
+ mach_msg_header_t* reply);
// This symbol must be visible to dlsym() - see
- // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
+ // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=345 for details.
kern_return_t catch_exception_raise(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
@@ -129,19 +145,19 @@ kern_return_t ForwardException(mach_port_t task,
#if TARGET_OS_IPHONE
// Implementation is based on the implementation generated by mig.
-boolean_t breakpad_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: routine() 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;
+boolean_t breakpad_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: routine() 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;
if (InHeadP->msgh_id != 2401) {
- ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
- ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
+ ((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
+ ((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
return FALSE;
}
@@ -171,8 +187,8 @@ boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
#pragma pack()
#endif
- Request *In0P = (Request *)InHeadP;
- Reply *OutP = (Reply *)OutHeadP;
+ Request* In0P = (Request*)InHeadP;
+ Reply* OutP = (Reply*)OutHeadP;
if (In0P->task.name != mach_task_self()) {
return FALSE;
@@ -186,8 +202,8 @@ boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
return TRUE;
}
#else
-boolean_t breakpad_exc_server(mach_msg_header_t *request,
- mach_msg_header_t *reply) {
+boolean_t breakpad_exc_server(mach_msg_header_t* request,
+ mach_msg_header_t* reply) {
return exc_server(request, reply);
}
@@ -207,9 +223,9 @@ kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
ExceptionHandler::ExceptionHandler(const string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
- void *callback_context,
+ void* callback_context,
bool install_handler,
- const char *port_name)
+ const char* port_name)
: dump_path_(),
filter_(filter),
callback_(callback),
@@ -235,7 +251,7 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
// 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,
+ void* callback_context,
bool install_handler)
: dump_path_(),
filter_(NULL),
@@ -269,9 +285,13 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
// Send an empty message to the handle port so that a minidump will
// be written
- SendMessageToHandlerThread(write_exception_stream ?
- kWriteDumpWithExceptionMessage :
- kWriteDumpMessage);
+ 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
@@ -287,18 +307,18 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
bool ExceptionHandler::WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
- void *callback_context) {
+ void* callback_context) {
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
- NULL);
+ 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) {
+ mach_port_t child_blamed_thread,
+ const string &dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
ScopedTaskSuspend suspend(child);
MinidumpGenerator generator(child, MACH_PORT_NULL);
@@ -306,34 +326,41 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
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__)
- EXC_ARM_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);
+ 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, result);
+ callback_context, result);
}
return result;
}
-bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
- int exception_code,
- int exception_subcode,
- mach_port_t thread_name,
- bool exit_after_write,
- bool report_current_thread) {
+bool ExceptionHandler::WriteMinidumpWithException(
+ int exception_type,
+ int exception_code,
+ int exception_subcode,
+ breakpad_ucontext_t* task_context,
+ mach_port_t thread_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
+
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
@@ -349,12 +376,15 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
// 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;
- return crash_generation_client_->RequestDumpForException(
- exception_type,
- exception_code,
- exception_subcode,
- thread_name);
+ return false;
+ result = crash_generation_client_->RequestDumpForException(
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name);
+ if (result && exit_after_write) {
+ _exit(exception_type);
+ }
}
#endif
} else {
@@ -366,6 +396,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
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.
@@ -432,12 +463,13 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
exception_behavior_t target_behavior = current.behaviors[found];
kern_return_t result;
+ // TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
+ // set. https://bugs.chromium.org/p/google-breakpad/issues/detail?id=551
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = 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;
@@ -448,9 +480,9 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
}
// static
-void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
- ExceptionHandler *self =
- reinterpret_cast<ExceptionHandler *>(exception_handler_class);
+void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
+ ExceptionHandler* self =
+ reinterpret_cast<ExceptionHandler*>(exception_handler_class);
ExceptionMessage receive;
// Wait for the exception info
@@ -495,11 +527,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
thread = receive.thread.name;
exception_type = EXC_BREAKPOINT;
-#if defined (__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__)
exception_code = EXC_I386_BPT;
-#elif defined (__ppc__) || defined (__ppc64__)
+#elif defined(__ppc__) || defined(__ppc64__)
exception_code = EXC_PPC_BREAKPOINT;
-#elif defined (__arm__)
+#elif defined(__arm__) || defined(__aarch64__)
exception_code = EXC_ARM_BREAKPOINT;
#else
#error architecture not supported
@@ -509,7 +541,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Write out the dump and save the result for later retrieval
self->last_minidump_write_result_ =
self->WriteMinidumpWithException(exception_type, exception_code,
- 0, thread,
+ 0, NULL, thread,
false, false);
#if USE_PROTECTED_ALLOCATIONS
@@ -544,8 +576,8 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
- subcode, receive.thread.name, true,
- false);
+ subcode, NULL, receive.thread.name,
+ true, false);
#if USE_PROTECTED_ALLOCATIONS
// This may have become protected again within
@@ -580,7 +612,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
return NULL;
}
-//static
+// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
@@ -590,6 +622,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
EXC_SOFTWARE,
MD_EXCEPTION_CODE_MAC_ABORT,
0,
+ static_cast<breakpad_ucontext_t*>(uc),
mach_thread_self(),
true,
true);
@@ -604,7 +637,6 @@ bool ExceptionHandler::InstallHandler() {
if (gProtectedData.handler != NULL) {
return false;
}
-#if TARGET_OS_IPHONE
if (!IsOutOfProcess()) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
@@ -624,7 +656,6 @@ bool ExceptionHandler::InstallHandler() {
mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
#endif
}
-#endif
try {
#if USE_PROTECTED_ALLOCATIONS
@@ -633,7 +664,6 @@ bool ExceptionHandler::InstallHandler() {
#else
previous_ = new ExceptionParameters();
#endif
-
}
catch (std::bad_alloc) {
return false;
@@ -732,7 +762,7 @@ bool ExceptionHandler::Setup(bool install_handler) {
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
}
- return result == KERN_SUCCESS ? true : false;
+ return result == KERN_SUCCESS;
}
bool ExceptionHandler::Teardown() {
diff --git a/3rdParty/Breakpad/src/client/mac/handler/exception_handler.h b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.h
index ec09134..f1d9ae9 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/exception_handler.h
+++ b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.h
@@ -41,7 +41,8 @@
#include <string>
-#include "processor/scoped_ptr.h"
+#include "client/mac/handler/ucontext_compat.h"
+#include "common/scoped_ptr.h"
#if !TARGET_OS_IPHONE
#include "client/mac/crash_generation/crash_generation_client.h"
@@ -182,10 +183,13 @@ class ExceptionHandler {
// success, false otherwise.
bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
- // All minidump writing goes through this one routine
+ // 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,
int exception_code,
int exception_subcode,
+ breakpad_ucontext_t *task_context,
mach_port_t thread_name,
bool exit_after_write,
bool report_current_thread);
diff --git a/3rdParty/Breakpad/src/client/mac/handler/mach_vm_compat.h b/3rdParty/Breakpad/src/client/mac/handler/mach_vm_compat.h
index e0459be..9e9028b 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/mach_vm_compat.h
+++ b/3rdParty/Breakpad/src/client/mac/handler/mach_vm_compat.h
@@ -32,15 +32,14 @@
#include <TargetConditionals.h>
-// On iOS 5, mach/mach_vm.h is not supported anymore. As the architecture is 32
-// bits, we can use the simple vm_ functions instead of the mach_vm_ ones.
+// 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 vm_region
-#define mach_vm_region_recurse vm_region_recurse
+#define mach_vm_region_recurse vm_region_recurse_64
#define mach_vm_size_t vm_size_t
#else
#include <mach/mach_vm.h>
diff --git a/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc
index b1d429c..48cd2e9 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc
+++ b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc
@@ -31,6 +31,7 @@
#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>
@@ -41,7 +42,7 @@
#include "client/mac/handler/minidump_generator.h"
-#ifdef HAS_ARM_SUPPORT
+#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
#include <mach/arm/thread_status.h>
#endif
#ifdef HAS_PPC_SUPPORT
@@ -61,7 +62,7 @@ using MacStringUtils::IntegerValueAtIndex;
namespace google_breakpad {
-#if __LP64__
+#if defined(__LP64__) && __LP64__
#define LC_SEGMENT_ARCH LC_SEGMENT_64
#else
#define LC_SEGMENT_ARCH LC_SEGMENT
@@ -77,6 +78,7 @@ MinidumpGenerator::MinidumpGenerator()
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()),
cpu_type_(DynamicImages::GetNativeCPUType()),
+ task_context_(NULL),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
GatherSystemInformation();
@@ -94,6 +96,7 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
crashing_task_(crashing_task),
handler_thread_(handler_thread),
cpu_type_(DynamicImages::GetNativeCPUType()),
+ task_context_(NULL),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
if (crashing_task != mach_task_self()) {
@@ -130,25 +133,47 @@ void MinidumpGenerator::GatherSystemInformation() {
vers_path,
kCFURLPOSIXPathStyle,
false);
- CFDataRef data;
- SInt32 error;
- CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
- &error);
-
+ 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) {
- CFRelease(sys_vers);
return;
}
-
- CFDictionaryRef list = static_cast<CFDictionaryRef>
- (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
- NULL));
+ CFDictionaryRef list =
+ static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(
+ NULL, data, kCFPropertyListImmutable, NULL, NULL));
+ CFRelease(data);
if (!list) {
- CFRelease(sys_vers);
- CFRelease(data);
return;
}
-
CFStringRef build_version = static_cast<CFStringRef>
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
CFStringRef product_version = static_cast<CFStringRef>
@@ -157,8 +182,6 @@ void MinidumpGenerator::GatherSystemInformation() {
string product_str = ConvertToString(product_version);
CFRelease(list);
- CFRelease(sys_vers);
- CFRelease(data);
strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
@@ -168,6 +191,10 @@ void MinidumpGenerator::GatherSystemInformation() {
os_build_number_ = IntegerValueAtIndex(product_str, 2);
}
+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);
@@ -288,7 +315,7 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
mach_vm_address_t proposed_next_region_base = next_region_base;
mach_vm_size_t next_region_size;
nesting_level = 0;
- mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+ 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);
@@ -362,6 +389,10 @@ bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
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);
@@ -386,6 +417,10 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
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);
@@ -403,13 +438,17 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
}
}
-u_int64_t MinidumpGenerator::CurrentPCForStack(
+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);
@@ -423,7 +462,7 @@ u_int64_t MinidumpGenerator::CurrentPCForStack(
return CurrentPCForStackX86_64(state);
#endif
default:
- assert("Unknown CPU type!");
+ assert(0 && "Unknown CPU type!");
return 0;
}
}
@@ -437,7 +476,7 @@ bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state,
return WriteStackFromStartAddress(start_addr, stack_location);
}
-u_int64_t
+uint64_t
MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
arm_thread_state_t *machine_state =
reinterpret_cast<arm_thread_state_t *>(state);
@@ -479,7 +518,82 @@ bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state,
AddGPR(10);
AddGPR(11);
AddGPR(12);
-#undef AddReg
+#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> 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 *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_ARM64_FULL;
+
+#define AddGPR(a) context_ptr->iregs[a] = \
+ 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;
@@ -503,7 +617,7 @@ bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
return WriteStackFromStartAddress(start_addr, stack_location);
}
-u_int64_t
+uint64_t
MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
@@ -511,7 +625,7 @@ MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}
-u_int64_t
+uint64_t
MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
@@ -533,8 +647,11 @@ bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
MDRawContextPPC *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
-#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
-#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+#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);
@@ -596,8 +713,11 @@ bool MinidumpGenerator::WriteContextPPC64(
MDRawContextPPC64 *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
-#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
-#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+#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);
@@ -661,11 +781,12 @@ bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
- mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
+ mach_vm_address_t start_addr = static_cast<mach_vm_address_t>(
+ REGISTER_FROM_THREADSTATE(machine_state, rsp));
return WriteStackFromStartAddress(start_addr, stack_location);
}
-u_int64_t
+uint64_t
MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
@@ -673,7 +794,7 @@ MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
return REGISTER_FROM_THREADSTATE(machine_state, eip);
}
-u_int64_t
+uint64_t
MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
@@ -694,7 +815,8 @@ bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
*register_location = context.location();
MDRawContextX86 *context_ptr = context.get();
-#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+#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);
@@ -733,7 +855,8 @@ bool MinidumpGenerator::WriteContextX86_64(
*register_location = context.location();
MDRawContextAMD64 *context_ptr = context.get();
-#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+#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);
@@ -757,7 +880,7 @@ bool MinidumpGenerator::WriteContextX86_64(
// 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<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
+ context_ptr->eflags = static_cast<uint32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
AddReg(cs);
AddReg(fs);
AddReg(gs);
@@ -770,6 +893,40 @@ bool MinidumpGenerator::WriteContextX86_64(
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
@@ -777,6 +934,11 @@ bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
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;
@@ -878,26 +1040,25 @@ bool MinidumpGenerator::WriteMemoryListStream(
mach_msg_type_number_t stateCount
= static_cast<mach_msg_type_number_t>(sizeof(state));
- if (thread_get_state(exception_thread_,
- BREAKPAD_MACHINE_THREAD_STATE,
- state,
- &stateCount) == KERN_SUCCESS) {
- u_int64_t ip = CurrentPCForStack(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 = ip;
+ 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,
- (vm_region_recurse_info_t)&info,
+ recurse_info,
&info_count);
if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
// Try to get 128 bytes before and after the IP, but
@@ -908,8 +1069,9 @@ bool MinidumpGenerator::WriteMemoryListStream(
uintptr_t end_of_range =
std::min(uintptr_t(ip + (kIPMemorySize / 2)),
uintptr_t(addr + size));
- ip_memory_d.memory.data_size =
- end_of_range - ip_memory_d.start_of_memory_range;
+ 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
@@ -921,7 +1083,7 @@ bool MinidumpGenerator::WriteMemoryListStream(
}
// Now fill in the memory list and write it.
- unsigned memory_count = memory_blocks_.size();
+ size_t memory_count = memory_blocks_.size();
if (!list.AllocateObjectAndArray(memory_count,
sizeof(MDMemoryDescriptor)))
return false;
@@ -929,7 +1091,7 @@ bool MinidumpGenerator::WriteMemoryListStream(
memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
memory_list_stream->location = list.location();
- list.get()->number_of_memory_ranges = memory_count;
+ list.get()->number_of_memory_ranges = static_cast<uint32_t>(memory_count);
unsigned int i;
for (i = 0; i < memory_count; ++i) {
@@ -1027,6 +1189,11 @@ bool MinidumpGenerator::WriteSystemInfoStream(
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;
+ break;
+#endif
#ifdef HAS_PPC_SUPPORT
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
@@ -1078,9 +1245,9 @@ bool MinidumpGenerator::WriteSystemInfoStream(
info_ptr->processor_level =
(info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
// 0xMMSS (Model, Stepping)
- info_ptr->processor_revision =
- (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
- ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
+ 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 ||
@@ -1103,7 +1270,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
break;
}
- info_ptr->number_of_processors = number_of_processors;
+ info_ptr->number_of_processors = static_cast<uint8_t>(number_of_processors);
#if TARGET_OS_IPHONE
info_ptr->platform_id = MD_OS_IOS;
#else
@@ -1142,13 +1309,13 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
return false;
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
- module->size_of_image = static_cast<u_int32_t>(image->GetVMSize());
+ 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 != (uint32_t)FindExecutableModule()) {
+ 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
@@ -1208,7 +1375,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
return false;
module->base_of_image = seg->vmaddr + slide;
- module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
+ module->size_of_image = static_cast<uint32_t>(seg->vmsize);
module->module_name_rva = string_location.rva;
bool in_memory = false;
@@ -1267,7 +1434,7 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
size_t module_name_length = strlen(module_name);
- if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
+ if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t)))
return false;
if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
@@ -1285,22 +1452,27 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
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, identifier);
+ result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
if (!result)
- result = macho.MD5(cpu_type, identifier);
+ result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
}
if (!result) {
FileID file_id(module_path);
- result = file_id.MachoIdentifier(cpu_type, identifier);
+ result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE,
+ identifier);
}
if (result) {
- cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
- (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
- (uint32_t)identifier[3];
- cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
- cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
+ 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];
@@ -1318,8 +1490,8 @@ bool MinidumpGenerator::WriteModuleListStream(
MDRawDirectory *module_list_stream) {
TypedMDRVA<MDRawModuleList> list(&writer_);
- size_t image_count = dynamic_images_ ?
- static_cast<size_t>(dynamic_images_->GetImageCount()) :
+ uint32_t image_count = dynamic_images_ ?
+ dynamic_images_->GetImageCount() :
_dyld_image_count();
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
@@ -1327,22 +1499,22 @@ bool MinidumpGenerator::WriteModuleListStream(
module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
module_list_stream->location = list.location();
- list.get()->number_of_modules = image_count;
+ list.get()->number_of_modules = static_cast<uint32_t>(image_count);
// Write out the executable module as the first one
MDRawModule module;
- size_t executableIndex = FindExecutableModule();
+ uint32_t executableIndex = FindExecutableModule();
- if (!WriteModuleStream(executableIndex, &module)) {
+ 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
- for (size_t i = 0; i < image_count; ++i) {
+ for (uint32_t i = 0; i < image_count; ++i) {
if (i != executableIndex) {
- if (!WriteModuleStream(i, &module)) {
+ if (!WriteModuleStream(static_cast<unsigned>(i), &module)) {
return false;
}
@@ -1363,7 +1535,7 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
misc_info_stream->location = info.location();
MDRawMiscInfo *info_ptr = info.get();
- info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo));
+ 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;
@@ -1376,18 +1548,18 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
if (getrusage(RUSAGE_SELF, &usage) != -1) {
// Omit the fractional time since the MDRawMiscInfo only wants seconds
info_ptr->process_user_time =
- static_cast<u_int32_t>(usage.ru_utime.tv_sec);
+ static_cast<uint32_t>(usage.ru_utime.tv_sec);
info_ptr->process_kernel_time =
- static_cast<u_int32_t>(usage.ru_stime.tv_sec);
+ 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) };
- u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
+ 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<u_int32_t>(proc.kp_proc.p_starttime.tv_sec);
+ static_cast<uint32_t>(proc.kp_proc.p_starttime.tv_sec);
}
// Speed
@@ -1395,11 +1567,11 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
const uint64_t kOneMillion = 1000 * 1000;
size = sizeof(speed);
sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
- info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion);
- info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion);
+ 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<u_int32_t>(speed / kOneMillion);
+ info_ptr->processor_current_mhz = static_cast<uint32_t>(speed / kOneMillion);
return true;
}
diff --git a/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.h b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.h
index 8394ce6..4e4b4a6 100644
--- a/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.h
+++ b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.h
@@ -37,6 +37,7 @@
#include <string>
+#include "client/mac/handler/ucontext_compat.h"
#include "client/minidump_file_writer.h"
#include "common/memory.h"
#include "common/mac/macho_utilities.h"
@@ -49,7 +50,9 @@
#define HAS_PPC_SUPPORT
#endif
#if defined(__arm__)
- #define HAS_ARM_SUPPORT
+#define HAS_ARM_SUPPORT
+#elif defined(__aarch64__)
+#define HAS_ARM64_SUPPORT
#elif defined(__i386__) || defined(__x86_64__)
#define HAS_X86_SUPPORT
#endif
@@ -102,6 +105,11 @@ class MinidumpGenerator {
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();
@@ -125,7 +133,7 @@ class MinidumpGenerator {
bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);
// Helpers
- u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
+ 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,
@@ -146,31 +154,38 @@ class MinidumpGenerator {
MDMemoryDescriptor *stack_location);
bool WriteContextARM(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
- u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
+ 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);
- u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
+ 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);
- u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
+ 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);
- u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
+ 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);
- u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
+ uint64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
#endif
// disallow copy ctor and operator=
@@ -192,13 +207,16 @@ class MinidumpGenerator {
// 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_;
-
+
+ // Context of the task to dump.
+ breakpad_ucontext_t *task_context_;
+
// Information about dynamically loaded code
DynamicImages *dynamic_images_;
diff --git a/3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.cc b/3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.cc
new file mode 100644
index 0000000..6142ad1
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.h b/3rdParty/Breakpad/src/client/mac/handler/protected_memory_allocator.h
new file mode 100644
index 0000000..7e188db
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/mac/handler/ucontext_compat.h b/3rdParty/Breakpad/src/client/mac/handler/ucontext_compat.h
new file mode 100644
index 0000000..1e4b752
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/minidump_file_writer.cc b/3rdParty/Breakpad/src/client/minidump_file_writer.cc
index c267410..a1957f3 100644
--- a/3rdParty/Breakpad/src/client/minidump_file_writer.cc
+++ b/3rdParty/Breakpad/src/client/minidump_file_writer.cc
@@ -40,10 +40,51 @@
#include "client/minidump_file_writer-inl.h"
#include "common/linux/linux_libc_support.h"
#include "common/string_conversion.h"
-#if __linux__
+#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);
@@ -62,7 +103,7 @@ MinidumpFileWriter::~MinidumpFileWriter() {
bool MinidumpFileWriter::Open(const char *path) {
assert(file_ == -1);
-#if __linux__
+#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);
@@ -75,16 +116,25 @@ 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 (-1 == ftruncate(file_, position_)) {
+#if defined(__ANDROID__)
+ if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
return false;
}
-#if __linux__
+#else
+ if (ftruncate(file_, position_)) {
+ return false;
+ }
+#endif
+#if defined(__linux__) && __linux__
result = (sys_close(file_) == 0);
#else
result = (close(file_) == 0);
@@ -99,11 +149,11 @@ bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
- if (sizeof(wchar_t) == sizeof(u_int16_t)) {
+ 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 {
- u_int16_t out[2];
+ uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
@@ -120,7 +170,7 @@ bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
// 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(u_int16_t) * out_count;
+ size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
@@ -132,7 +182,7 @@ bool MinidumpFileWriter::CopyStringToMDString(const char *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
- u_int16_t out[2];
+ uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
@@ -147,7 +197,7 @@ bool MinidumpFileWriter::CopyStringToMDString(const char *str,
// Append the one or two UTF-16 characters
int out_count = out[1] ? 2 : 1;
- size_t out_size = sizeof(u_int16_t) * out_count;
+ size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
@@ -170,17 +220,17 @@ bool MinidumpFileWriter::WriteStringCore(const CharType *str,
// Allocate the string buffer
TypedMDRVA<MDString> mdstring(this);
- if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t)))
+ if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
return false;
// Set length excluding the NULL and copy the string
mdstring.get()->length =
- static_cast<u_int32_t>(mdstring_length * sizeof(u_int16_t));
+ static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
// NULL terminate
if (result) {
- u_int16_t ch = 0;
+ uint16_t ch = 0;
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
if (result)
@@ -211,7 +261,7 @@ bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
if (!mem.Copy(src, mem.size()))
return false;
- output->start_of_memory_range = reinterpret_cast<u_int64_t>(src);
+ output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
output->memory = mem.location();
return true;
@@ -220,6 +270,20 @@ bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
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_) {
@@ -253,17 +317,19 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
return false;
// Seek and write the data
-#if __linux__
+#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) {
-#endif
return true;
}
}
-
+#endif
return false;
}
diff --git a/3rdParty/Breakpad/src/client/minidump_file_writer.h b/3rdParty/Breakpad/src/client/minidump_file_writer.h
index 313b250..ce32b6d 100644
--- a/3rdParty/Breakpad/src/client/minidump_file_writer.h
+++ b/3rdParty/Breakpad/src/client/minidump_file_writer.h
@@ -74,8 +74,8 @@ public:
MinidumpFileWriter();
~MinidumpFileWriter();
- // Open |path| as the destination of the minidump data. Any existing file
- // will be overwritten.
+ // 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);
@@ -172,7 +172,7 @@ class UntypedMDRVA {
// Return size and position
inline MDLocationDescriptor location() const {
- MDLocationDescriptor location = { static_cast<u_int32_t>(size_),
+ MDLocationDescriptor location = { static_cast<uint32_t>(size_),
position_ };
return location;
}
diff --git a/3rdParty/Breakpad/src/client/minidump_file_writer_unittest.cc b/3rdParty/Breakpad/src/client/minidump_file_writer_unittest.cc
new file mode 100644
index 0000000..60c364e
--- /dev/null
+++ b/3rdParty/Breakpad/src/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.c \
+ ../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/3rdParty/Breakpad/src/client/windows/common/auto_critical_section.h b/3rdParty/Breakpad/src/client/windows/common/auto_critical_section.h
new file mode 100644
index 0000000..3fd4b9b
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/windows/common/ipc_protocol.h b/3rdParty/Breakpad/src/client/windows/common/ipc_protocol.h
index b03c032..c748681 100644
--- a/3rdParty/Breakpad/src/client/windows/common/ipc_protocol.h
+++ b/3rdParty/Breakpad/src/client/windows/common/ipc_protocol.h
@@ -30,8 +30,8 @@
#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
-#include <Windows.h>
-#include <DbgHelp.h>
+#include <windows.h>
+#include <dbghelp.h>
#include <string>
#include <utility>
#include "common/windows/string_utils-inl.h"
diff --git a/3rdParty/Breakpad/src/client/windows/crash_generation/ReadMe.txt b/3rdParty/Breakpad/src/client/windows/crash_generation/ReadMe.txt
new file mode 100644
index 0000000..b54d0e1
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.cc b/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.cc
new file mode 100644
index 0000000..ed31263
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.cc
@@ -0,0 +1,223 @@
+// 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 "client/windows/crash_generation/client_info.h"
+#include "client/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) const {
+ SIZE_T bytes_count = 0;
+ if (!ReadProcessMemory(process_handle_,
+ ex_info_,
+ ex_info,
+ sizeof(*ex_info),
+ &bytes_count)) {
+ return false;
+ }
+
+ return bytes_count == sizeof(*ex_info);
+}
+
+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/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.h b/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.h
new file mode 100644
index 0000000..6a8fba3
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/client_info.h
@@ -0,0 +1,177 @@
+// 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 "client/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) 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/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc
index b0d3d04..3ba5d4e 100644
--- a/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc
@@ -96,14 +96,14 @@ CrashGenerationClient::CrashGenerationClient(
const CustomClientInfo* custom_info)
: pipe_name_(pipe_name),
pipe_handle_(NULL),
+ custom_info_(),
dump_type_(dump_type),
- thread_id_(0),
- server_process_id_(0),
crash_event_(NULL),
crash_generated_(NULL),
server_alive_(NULL),
- exception_pointers_(NULL),
- custom_info_() {
+ server_process_id_(0),
+ thread_id_(0),
+ exception_pointers_(NULL) {
memset(&assert_info_, 0, sizeof(assert_info_));
if (custom_info) {
custom_info_ = *custom_info;
@@ -116,14 +116,14 @@ CrashGenerationClient::CrashGenerationClient(
const CustomClientInfo* custom_info)
: pipe_name_(),
pipe_handle_(pipe_handle),
+ custom_info_(),
dump_type_(dump_type),
- thread_id_(0),
- server_process_id_(0),
crash_event_(NULL),
crash_generated_(NULL),
server_alive_(NULL),
- exception_pointers_(NULL),
- custom_info_() {
+ server_process_id_(0),
+ thread_id_(0),
+ exception_pointers_(NULL) {
memset(&assert_info_, 0, sizeof(assert_info_));
if (custom_info) {
custom_info_ = *custom_info;
@@ -178,6 +178,10 @@ CrashGenerationClient::~CrashGenerationClient() {
//
// Returns true if the registration is successful; false otherwise.
bool CrashGenerationClient::Register() {
+ if (IsRegistered()) {
+ return true;
+ }
+
HANDLE pipe = ConnectToServer();
if (!pipe) {
return false;
diff --git a/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.h b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.h
index 2ce14dd..457f731 100644
--- a/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.h
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.h
@@ -35,7 +35,7 @@
#include <string>
#include <utility>
#include "client/windows/common/ipc_protocol.h"
-#include "processor/scoped_ptr.h"
+#include "common/scoped_ptr.h"
namespace google_breakpad {
diff --git a/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.cc
new file mode 100644
index 0000000..0af213b
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.cc
@@ -0,0 +1,943 @@
+// 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 "client/windows/crash_generation/crash_generation_server.h"
+#include <windows.h>
+#include <cassert>
+#include <list>
+#include "client/windows/common/auto_critical_section.h"
+#include "common/scoped_ptr.h"
+
+#include "client/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,
+ 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),
+ 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) {
+ 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);
+
+ // 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);
+
+ // New scope to hold the lock for the shortest time.
+ {
+ AutoCriticalSection lock(&sync_);
+ 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);
+ }
+
+ // 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) {
+ std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path;
+ dump_callback_(dump_context_, &client_info, ptr_dump_path);
+ }
+
+ SetEvent(client_info.dump_generated_handle());
+}
+
+bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
+ std::wstring* dump_path) {
+ assert(client.pid() != 0);
+ assert(client.process_handle());
+
+ // We have to get the address of EXCEPTION_INFORMATION from
+ // the client process address space.
+ EXCEPTION_POINTERS* client_ex_info = NULL;
+ if (!client.GetClientExceptionInfo(&client_ex_info)) {
+ return false;
+ }
+
+ DWORD client_thread_id = 0;
+ if (!client.GetClientThreadId(&client_thread_id)) {
+ return false;
+ }
+
+ MinidumpGenerator dump_generator(dump_path_,
+ client.process_handle(),
+ client.pid(),
+ client_thread_id,
+ GetCurrentThreadId(),
+ client_ex_info,
+ client.assert_info(),
+ client.dump_type(),
+ true);
+
+ 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/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.h b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.h
new file mode 100644
index 0000000..0ea90e5
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_server.h
@@ -0,0 +1,299 @@
+// 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 "client/windows/common/ipc_protocol.h"
+#include "client/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 (*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 crash_callback: Callback for a client crash dump request.
+ // Parameter crash_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 std::wstring& pipe_name,
+ SECURITY_ATTRIBUTES* pipe_sec_attrs,
+ OnClientConnectedCallback connect_callback,
+ void* connect_context,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ 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;
+ }
+
+ 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 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_;
+
+ // 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/3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.cc b/3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.cc
new file mode 100644
index 0000000..100e365
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.cc
@@ -0,0 +1,583 @@
+// 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 "client/windows/crash_generation/minidump_generator.h"
+
+#include <assert.h>
+#include <avrfsdk.h>
+
+#include <algorithm>
+#include <iterator>
+#include <list>
+#include <vector>
+
+#include "client/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(),
+#ifdef _MSC_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_pointers = NULL;
+ 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_pointers = &dump_exception_info;
+ 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/3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.h b/3rdParty/Breakpad/src/client/windows/crash_generation/minidump_generator.h
new file mode 100644
index 0000000..a707c0b
--- /dev/null
+++ b/3rdParty/Breakpad/src/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/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc
index 272ca5f..b3daa0c 100644
--- a/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc
+++ b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.cc
@@ -27,7 +27,7 @@
// (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 <objbase.h>
#include <algorithm>
#include <cassert>
@@ -41,16 +41,17 @@
namespace google_breakpad {
-static const int kWaitForHandlerThreadMs = 60000;
-static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
-
// This is passed as the context to the MinidumpWriteDump callback.
typedef struct {
- ULONG64 memory_base;
- ULONG memory_size;
- bool finished;
+ AppMemoryList::const_iterator iter;
+ AppMemoryList::const_iterator end;
} MinidumpCallbackContext;
+// 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_;
@@ -71,7 +72,8 @@ ExceptionHandler::ExceptionHandler(const wstring& dump_path,
handler_types,
dump_type,
pipe_name,
- NULL,
+ NULL, // pipe_handle
+ NULL, // crash_generation_client
custom_info);
}
@@ -89,10 +91,32 @@ ExceptionHandler::ExceptionHandler(const wstring& dump_path,
callback_context,
handler_types,
dump_type,
- NULL,
+ 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,
@@ -105,20 +129,23 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
callback_context,
handler_types,
MiniDumpNormal,
- NULL,
- NULL,
- NULL);
+ 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,
- const CustomClientInfo* 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;
@@ -147,24 +174,22 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
assertion_ = NULL;
handler_return_value_ = false;
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));
+ }
- // Attempt to use out-of-process if user has specified a pipe.
- if (pipe_name != NULL || pipe_handle != NULL) {
- assert(!(pipe_name && pipe_handle));
-
- scoped_ptr<CrashGenerationClient> client;
- if (pipe_name) {
- client.reset(
- new CrashGenerationClient(pipe_name,
- dump_type_,
- custom_info));
- } else {
- 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()) {
@@ -192,6 +217,7 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
// 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,
@@ -222,6 +248,12 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
set_dump_path(dump_path);
}
+ // Reserve one element for the instruction memory
+ AppMemory instruction_memory;
+ instruction_memory.ptr = NULL;
+ instruction_memory.length = 0;
+ 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
@@ -323,6 +355,7 @@ ExceptionHandler::~ExceptionHandler() {
// inside DllMain.
is_shutdown_ = true;
ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
+ const int kWaitForHandlerThreadMs = 60000;
WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
#else
TerminateThread(handler_thread_, 1);
@@ -450,7 +483,14 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
DWORD code = exinfo->ExceptionRecord->ExceptionCode;
LONG action;
bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
- (code == EXCEPTION_SINGLE_STEP);
+ (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;
+ }
bool success = false;
@@ -729,12 +769,72 @@ bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) {
// static
bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
MinidumpCallback callback,
- void* callback_context) {
+ void* callback_context,
+ MINIDUMP_TYPE dump_type) {
ExceptionHandler handler(dump_path, NULL, callback, callback_context,
- HANDLER_NONE);
+ 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) {
+ success = callback(handler.dump_path_c_, handler.next_minidump_id_c_,
+ callback_context, NULL, NULL, success);
+ }
+
+ return success;
+}
+
bool ExceptionHandler::WriteMinidumpWithException(
DWORD requesting_thread_id,
EXCEPTION_POINTERS* exinfo,
@@ -753,106 +853,11 @@ bool ExceptionHandler::WriteMinidumpWithException(
if (IsOutOfProcess()) {
success = crash_generation_client_->RequestDump(exinfo, assertion);
} else {
- 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;
-
- // 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;
-
- // Leave room in user_stream_array for a possible assertion info stream.
- MINIDUMP_USER_STREAM user_stream_array[2];
- 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;
-
- if (assertion) {
- user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
- user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
- user_stream_array[1].Buffer = assertion;
- ++user_streams.UserStreamCount;
- }
-
- MINIDUMP_CALLBACK_INFORMATION callback;
- MinidumpCallbackContext context;
- MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
- // Older versions of DbgHelp.dll don't correctly put the memory around
- // the faulting instruction pointer into the minidump. This
- // callback will ensure that it gets included.
- if (exinfo) {
- // Find a memory region of 256 bytes centered on the
- // faulting instruction pointer.
- const ULONG64 instruction_pointer =
-#if defined(_M_IX86)
- exinfo->ContextRecord->Eip;
-#elif defined(_M_AMD64)
- exinfo->ContextRecord->Rip;
-#else
-#error Unsupported platform
-#endif
-
- MEMORY_BASIC_INFORMATION info;
- if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
- &info,
- sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
- info.State == MEM_COMMIT) {
- // Attempt to get 128 bytes before and after the instruction
- // pointer, but settle for whatever's available up to the
- // boundaries of the memory region.
- const ULONG64 kIPMemorySize = 256;
- context.memory_base =
- (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
- instruction_pointer - (kIPMemorySize / 2));
- ULONG64 end_of_range =
- (std::min)(instruction_pointer + (kIPMemorySize / 2),
- reinterpret_cast<ULONG64>(info.BaseAddress)
- + info.RegionSize);
- context.memory_size =
- static_cast<ULONG>(end_of_range - context.memory_base);
-
- context.finished = false;
- callback.CallbackRoutine = MinidumpWriteDumpCallback;
- callback.CallbackParam = reinterpret_cast<void*>(&context);
- callback_pointer = &callback;
- }
- }
-
- // The explicit comparison to TRUE avoids a warning (C4800).
- success = (minidump_write_dump_(GetCurrentProcess(),
- GetCurrentProcessId(),
- dump_file,
- dump_type_,
- exinfo ? &except_info : NULL,
- &user_streams,
- callback_pointer) == TRUE);
-
- CloseHandle(dump_file);
- }
- }
+ success = WriteMinidumpWithExceptionForProcess(requesting_thread_id,
+ exinfo,
+ assertion,
+ GetCurrentProcess(),
+ true);
}
if (callback_) {
@@ -876,16 +881,16 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
case MemoryCallback: {
MinidumpCallbackContext* callback_context =
reinterpret_cast<MinidumpCallbackContext*>(context);
- if (callback_context->finished)
+ if (callback_context->iter == callback_context->end)
return FALSE;
// Include the specified memory region.
- callback_output->MemoryBase = callback_context->memory_base;
- callback_output->MemorySize = callback_context->memory_size;
- callback_context->finished = true;
+ 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:
@@ -906,6 +911,132 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
return FALSE;
}
+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;
+ }
+
+ // Older versions of DbgHelp.dll don't correctly put the memory around
+ // the faulting instruction pointer into the minidump. This
+ // callback will ensure that it gets included.
+ if (exinfo) {
+ // Find a memory region of 256 bytes centered on the
+ // faulting instruction pointer.
+ const ULONG64 instruction_pointer =
+#if defined(_M_IX86)
+ exinfo->ContextRecord->Eip;
+#elif defined(_M_AMD64)
+ exinfo->ContextRecord->Rip;
+#else
+#error Unsupported platform
+#endif
+
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQueryEx(process,
+ reinterpret_cast<LPCVOID>(instruction_pointer),
+ &info,
+ sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
+ info.State == MEM_COMMIT) {
+ // Attempt to get 128 bytes before and after the instruction
+ // pointer, but settle for whatever's available up to the
+ // boundaries of the memory region.
+ const ULONG64 kIPMemorySize = 256;
+ ULONG64 base =
+ (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
+ instruction_pointer - (kIPMemorySize / 2));
+ ULONG64 end_of_range =
+ (std::min)(instruction_pointer + (kIPMemorySize / 2),
+ reinterpret_cast<ULONG64>(info.BaseAddress)
+ + info.RegionSize);
+ ULONG size = static_cast<ULONG>(end_of_range - base);
+
+ AppMemory& elt = app_memory_info_.front();
+ elt.ptr = base;
+ elt.length = size;
+ }
+ }
+
+ MinidumpCallbackContext context;
+ context.iter = app_memory_info_.begin();
+ context.end = app_memory_info_.end();
+
+ // 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};
@@ -926,4 +1057,26 @@ void ExceptionHandler::UpdateNextID() {
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_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);
+ }
+}
+
} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/client/windows/handler/exception_handler.h b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.h
index 6f59348..2939a68 100644
--- a/3rdParty/Breakpad/src/client/windows/handler/exception_handler.h
+++ b/3rdParty/Breakpad/src/client/windows/handler/exception_handler.h
@@ -57,27 +57,44 @@
#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
#include <stdlib.h>
-#include <Windows.h>
-#include <DbgHelp.h>
+#include <windows.h>
+#include <dbghelp.h>
#include <rpc.h>
-#pragma warning( push )
+#pragma warning(push)
// Disable exception handler warnings.
-#pragma warning( disable : 4530 )
+#pragma warning(disable:4530)
+#include <list>
#include <string>
#include <vector>
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/crash_generation/crash_generation_client.h"
+#include "common/scoped_ptr.h"
#include "google_breakpad/common/minidump_format.h"
-#include "processor/scoped_ptr.h"
namespace google_breakpad {
using std::vector;
using std::wstring;
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+ ULONG64 ptr;
+ ULONG length;
+
+ bool operator==(const struct AppMemory& other) const {
+ return ptr == other.ptr;
+ }
+
+ bool operator==(const void* other) const {
+ return ptr == reinterpret_cast<ULONG64>(other);
+ }
+};
+typedef std::list<AppMemory> AppMemoryList;
+
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
@@ -178,6 +195,25 @@ class ExceptionHandler {
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.
@@ -208,7 +244,20 @@ class ExceptionHandler {
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const wstring &dump_path,
- MinidumpCallback callback, void* callback_context);
+ 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
@@ -222,9 +271,23 @@ class ExceptionHandler {
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);
+
private:
friend class AutoExceptionHandler;
@@ -237,6 +300,7 @@ class ExceptionHandler {
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
@@ -289,8 +353,9 @@ class ExceptionHandler {
bool WriteMinidumpOnHandlerThread(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
+ // 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.
@@ -305,6 +370,20 @@ class ExceptionHandler {
const PMINIDUMP_CALLBACK_INPUT callback_input,
PMINIDUMP_CALLBACK_OUTPUT callback_output);
+ // 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();
@@ -414,6 +493,14 @@ class ExceptionHandler {
// 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
@@ -433,7 +520,7 @@ class ExceptionHandler {
static CRITICAL_SECTION handler_stack_critical_section_;
// The number of instances of this class.
- volatile static LONG instance_count_;
+ static volatile LONG instance_count_;
// disallow copy ctor and operator=
explicit ExceptionHandler(const ExceptionHandler &);
@@ -442,6 +529,6 @@ class ExceptionHandler {
} // namespace google_breakpad
-#pragma warning( pop )
+#pragma warning(pop)
#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__