diff options
author | Tobias Markmann <tm@ayena.de> | 2018-03-20 13:12:10 (GMT) |
---|---|---|
committer | Tobias Markmann <tm@ayena.de> | 2018-03-20 14:48:04 (GMT) |
commit | f2c2c7d035029fb9615b42c18ccea83e8e705b10 (patch) | |
tree | 2f56fb77de0f366528395f21732d418f016f63b5 /3rdParty/Breakpad/src/client/mac | |
parent | 44581c5285d13c0ec715b35ddc79177e5ebeef39 (diff) | |
parent | 5ba3f18ad8efa040d49f36d83ec2e7891a9add9f (diff) | |
download | swift-swift-5.0alpha2.zip swift-swift-5.0alpha2.tar.bz2 |
Merge branch 'swift-4.x'swift-5.0alpha2
* swift-4.x: (44 commits)
Test-Information:
Builds on macOS 10.13.3 with clang trunk.
Change-Id: If50381f103b0ad18d038b920d3d43537642141cb
Diffstat (limited to '3rdParty/Breakpad/src/client/mac')
18 files changed, 1672 insertions, 187 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(¶meter_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_ |