1# HG changeset patch 2# User Steven Michaud <smichaud@pobox.com> 3# Date 1619800781 18000 4# Fri Apr 30 11:39:41 2021 -0500 5# Node ID 9f89eb3d68316e8c3a469d1c058ad40c1807d7bc 6# Parent 0db412525773fff333e8d338551021e083c25619 7Bug 1577886 - Add support to for macOS __crash_info data to Breakpad. r=gsvelto 8Differential Revision: https://phabricator.services.mozilla.com/D112871 9 10diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h 11--- a/src/google_breakpad/common/minidump_format.h 12+++ b/src/google_breakpad/common/minidump_format.h 13@@ -351,6 +351,10 @@ typedef enum { 14 /* Crashpad extension types. 0x4350 = "CP" 15 * See Crashpad's minidump/minidump_extensions.h. */ 16 MD_CRASHPAD_INFO_STREAM = 0x43500001, /* MDRawCrashpadInfo */ 17+ 18+ /* Data from the __DATA,__crash_info section of every module which contains 19+ * one that has useful data. Only available on macOS. 0x4D7A = "Mz". */ 20+ MOZ_MACOS_CRASH_INFO_STREAM = 0x4d7a0001, 21 } MDStreamType; /* MINIDUMP_STREAM_TYPE */ 22 23 24@@ -1094,6 +1098,52 @@ typedef struct { 25 MDLocationDescriptor module_list; /* MDRawModuleCrashpadInfoList */ 26 } MDRawCrashpadInfo; 27 28+/* macOS __DATA,__crash_info data */ 29+ 30+typedef struct { 31+ uint64_t stream_type; /* MOZ_MACOS_CRASH_INFO_STREAM */ 32+ uint64_t version; 33+ uint64_t thread; 34+ uint64_t dialog_mode; 35+ uint64_t abort_cause; /* Only valid when 'version' > 4 */ 36+ /* If/when Apple adds more fields to crashreporter_annotations_t, add 37+ * numerical fields here and change (MDRawMacCrashInfo).record_start_size 38+ * accordingly. Make them all uint64_t, to keep this structure the same size 39+ * on all platforms. 'data' should always be the last field. Add new string 40+ * fields to the end of 'data'. */ 41+ /* 'data' currently contains five null-terminated uint8_t arrays, each 42+ * possibly empty (containing only a single terminal null), stored one after 43+ * the other: 44+ * module_path; 45+ * message; 46+ * signature_string; 47+ * backtrace; 48+ * message2; */ 49+ uint8_t data[0]; 50+} MDRawMacCrashInfoRecord; 51+ 52+/* This is the maximum supported size for each string in 53+ * (MDRawMacCrashInfoRecord).data. If we encounter a string in the 54+ * __crash_info section which seems larger than this, that's a sign of data 55+ * corruption. */ 56+#define MACCRASHINFO_STRING_MAXSIZE 8192 57+ 58+/* In principle there should only be one or two non-empty __DATA,__crash_info 59+ * sections per process. But the __crash_info section is almost entirely 60+ * undocumented, so just in case we set a large maximum. */ 61+#define MAC_CRASH_INFOS_MAX 20 62+ 63+typedef struct { 64+ uint32_t stream_type; /* MOZ_MACOS_CRASH_INFO_STREAM */ 65+ uint32_t record_count; 66+ /* The size of the "fixed-size" part of MDRawMacCrashInfoRecord, before the 67+ * 'data' field. This will always be 'sizeof(MDRawMacCrashInfoRecord)'. But 68+ * that value may change if more numerical fields are added to 69+ * MDRawMacCrashInfoRecord in the future. */ 70+ uint32_t record_start_size; 71+ MDLocationDescriptor records[MAC_CRASH_INFOS_MAX]; 72+} MDRawMacCrashInfo; 73+ 74 #if defined(_MSC_VER) 75 #pragma warning(pop) 76 #endif /* _MSC_VER */ 77diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h 78--- a/src/google_breakpad/processor/minidump.h 79+++ b/src/google_breakpad/processor/minidump.h 80@@ -1151,6 +1151,57 @@ class MinidumpCrashpadInfo : public Mini 81 std::map<std::string, std::string> simple_annotations_; 82 }; 83 84+// MinidumpMacCrashInfo wraps MDRawMacCrashInfo. It's an optional stream 85+// in a minidump that records information from the __DATA,__crash_info 86+// section of every module in the crashing process that contains one, and 87+// which isn't empty of useful information. Only present on macOS. 88+ 89+// Friendly wrapper for the information in MDRawMacCrashInfoRecord. 90+typedef struct crash_info_record { 91+ string module_path; 92+ unsigned long version; 93+ string message; 94+ string signature_string; 95+ string backtrace; 96+ string message2; 97+ unsigned long long thread; 98+ unsigned int dialog_mode; 99+ long long abort_cause; // Only valid when 'version' > 4 100+ crash_info_record() 101+ : version(0), thread(0), dialog_mode(0), abort_cause(0) 102+ {} 103+} crash_info_record_t; 104+ 105+class MinidumpMacCrashInfo : public MinidumpStream { 106+ public: 107+ // A human-readable representation of the data from the __DATA,__crash_info 108+ // sections in all of the crashing process's modules that have one, if 109+ // it's not empty of useful data. Suitable for use by "minidump_stackwalk". 110+ string description() const { return description_; } 111+ // A "machine-readable" copy of the same information, suitable for use by 112+ // "minidump_stalkwalk -m". 113+ vector<crash_info_record_t> const records() { 114+ return records_; 115+ } 116+ 117+ // Print a human-readable representation of the object to stdout. 118+ void Print(); 119+ 120+ private: 121+ friend class Minidump; 122+ 123+ static const uint32_t kStreamType = MOZ_MACOS_CRASH_INFO_STREAM; 124+ 125+ explicit MinidumpMacCrashInfo(Minidump* minidump_); 126+ 127+ bool ReadCrashInfoRecord(MDLocationDescriptor location, 128+ uint32_t record_start_size); 129+ bool Read(uint32_t expected_size); 130+ 131+ string description_; 132+ vector<crash_info_record_t> records_; 133+}; 134+ 135 136 // Minidump is the user's interface to a minidump file. It wraps MDRawHeader 137 // and provides access to the minidump's top-level stream directory. 138@@ -1214,6 +1265,7 @@ class Minidump { 139 virtual MinidumpBreakpadInfo* GetBreakpadInfo(); 140 virtual MinidumpMemoryInfoList* GetMemoryInfoList(); 141 MinidumpCrashpadInfo* GetCrashpadInfo(); 142+ MinidumpMacCrashInfo* GetMacCrashInfo(); 143 144 // The next method also calls GetStream, but is exclusive for Linux dumps. 145 virtual MinidumpLinuxMapsList *GetLinuxMapsList(); 146diff --git a/src/google_breakpad/processor/process_state.h b/src/google_breakpad/processor/process_state.h 147--- a/src/google_breakpad/processor/process_state.h 148+++ b/src/google_breakpad/processor/process_state.h 149@@ -112,6 +112,14 @@ class ProcessState { 150 return &thread_memory_regions_; 151 } 152 const SystemInfo* system_info() const { return &system_info_; } 153+ string mac_crash_info() const { return mac_crash_info_; } 154+ size_t mac_crash_info_records_count() const { 155+ return mac_crash_info_records_.size(); 156+ } 157+ const crash_info_record_t* mac_crash_info_records() const { 158+ return reinterpret_cast<const crash_info_record_t*>( 159+ &mac_crash_info_records_[0]); 160+ } 161 const CodeModules* modules() const { return modules_; } 162 const CodeModules* unloaded_modules() const { return unloaded_modules_; } 163 const vector<linked_ptr<const CodeModule> >* shrunk_range_modules() const { 164@@ -179,6 +187,10 @@ class ProcessState { 165 // OS and CPU information. 166 SystemInfo system_info_; 167 168+ // Information from __DATA,__crash_info sections. Only present on macOS. 169+ string mac_crash_info_; 170+ vector<crash_info_record_t> mac_crash_info_records_; 171+ 172 // The modules that were loaded into the process represented by the 173 // ProcessState. 174 const CodeModules *modules_; 175diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc 176--- a/src/processor/minidump.cc 177+++ b/src/processor/minidump.cc 178@@ -5116,6 +5116,230 @@ void MinidumpCrashpadInfo::Print() { 179 printf("\n"); 180 } 181 182+// 183+// MinidumpMacCrashInfo 184+// 185+ 186+MinidumpMacCrashInfo::MinidumpMacCrashInfo(Minidump* minidump) 187+ : MinidumpStream(minidump), 188+ description_(), 189+ records_() { 190+} 191+ 192+bool MinidumpMacCrashInfo::ReadCrashInfoRecord(MDLocationDescriptor location, 193+ uint32_t record_start_size) { 194+ if (!minidump_->SeekSet(location.rva)) { 195+ BPLOG(ERROR) << "ReadCrashInfoRecord could not seek to record"; 196+ return false; 197+ } 198+ 199+ // We may be reading a minidump 1) created by (newer) code that defines more 200+ // fields than we do in the fixed-size part of MDRawMacCrashInfoRecord 201+ // (before 'data'), or 2) created by (older) code that defines fewer fields. 202+ // In the first case we read in the newer fields but ignore them. In the 203+ // second case we read in only the older fields, and leave the newer fields 204+ // (in 'raw_record_start') set to zero. 205+ uint32_t raw_record_size = sizeof(MDRawMacCrashInfoRecord); 206+ if (record_start_size > raw_record_size) { 207+ raw_record_size = record_start_size; 208+ } 209+ scoped_ptr< vector<uint8_t> > raw_record( 210+ new vector<uint8_t>(raw_record_size)); 211+ if (!minidump_->ReadBytes(&(*raw_record)[0], record_start_size)) { 212+ BPLOG(ERROR) << "ReadCrashInfoRecord could not read " << 213+ record_start_size << " bytes of record"; 214+ return false; 215+ } 216+ MDRawMacCrashInfoRecord* raw_record_start = 217+ (MDRawMacCrashInfoRecord*) &(*raw_record)[0]; 218+ 219+ if (minidump_->swap()) { 220+ Swap(&raw_record_start->stream_type); 221+ Swap(&raw_record_start->version); 222+ Swap(&raw_record_start->thread); 223+ Swap(&raw_record_start->dialog_mode); 224+ Swap(&raw_record_start->abort_cause); 225+ } 226+ 227+ if (raw_record_start->stream_type != MOZ_MACOS_CRASH_INFO_STREAM) { 228+ BPLOG(ERROR) << "ReadCrashInfoRecord stream type mismatch, " << 229+ raw_record_start->stream_type << " != " << 230+ MOZ_MACOS_CRASH_INFO_STREAM; 231+ return false; 232+ } 233+ 234+ uint32_t string_data_size = location.data_size - record_start_size; 235+ scoped_ptr< vector<uint8_t> > data(new vector<uint8_t>(string_data_size)); 236+ if (!minidump_->ReadBytes(&(*data)[0], string_data_size)) { 237+ BPLOG(ERROR) << "ReadCrashInfoRecord could not read " << 238+ string_data_size << " bytes of record data"; 239+ return false; 240+ } 241+ 242+ crash_info_record_t record; 243+ 244+ record.version = (unsigned long) raw_record_start->version; 245+ record.thread = (unsigned long long) raw_record_start->thread; 246+ record.dialog_mode = (unsigned int) raw_record_start->dialog_mode; 247+ record.abort_cause = (long long) raw_record_start->abort_cause; 248+ 249+ // Once again, we may be reading a minidump created by newer code that 250+ // stores more strings than we expect in (MDRawMacCrashInfoRecord).data, 251+ // or one created by older code that contains fewer strings than we 252+ // expect. In the first case we ignore the "extra" strings. To deal with 253+ // the second case we bail when 'offset >= string_data_size'. 254+ const char* string_data = (const char*) &(*data)[0]; 255+ size_t offset = 0; 256+ for (int i = 1; i <= 5; ++i) { 257+ switch (i) { 258+ case 1: 259+ record.module_path.append(string_data); 260+ break; 261+ case 2: 262+ record.message.append(string_data); 263+ break; 264+ case 3: 265+ record.signature_string.append(string_data); 266+ break; 267+ case 4: 268+ record.backtrace.append(string_data); 269+ break; 270+ case 5: 271+ record.message2.append(string_data); 272+ break; 273+ } 274+ size_t char_array_size = strlen(string_data) + 1; 275+ offset += char_array_size; 276+ if (offset >= string_data_size) { 277+ break; 278+ } 279+ string_data += char_array_size; 280+ } 281+ 282+ records_.push_back(record); 283+ 284+ description_.append(" Module \""); 285+ description_.append(record.module_path); 286+ description_.append("\":\n"); 287+ 288+ int num_fields = 6; 289+ if (record.version > 4) { 290+ num_fields = 7; 291+ } 292+ for (int i = 1; i <= num_fields; ++i) { 293+ switch (i) { 294+ case 1: 295+ if (!record.message.empty()) { 296+ description_.append(" message: \""); 297+ description_.append(record.message); 298+ description_.append("\"\n"); 299+ } 300+ break; 301+ case 2: 302+ if (!record.signature_string.empty()) { 303+ description_.append(" signature_string: \""); 304+ description_.append(record.signature_string); 305+ description_.append("\"\n"); 306+ } 307+ break; 308+ case 3: 309+ if (!record.backtrace.empty()) { 310+ description_.append(" backtrace: \""); 311+ description_.append(record.backtrace); 312+ description_.append("\"\n"); 313+ } 314+ break; 315+ case 4: 316+ if (!record.message2.empty()) { 317+ description_.append(" message2: \""); 318+ description_.append(record.message2); 319+ description_.append("\"\n"); 320+ } 321+ break; 322+ case 5: 323+ if (record.thread) { 324+ char thread[128]; 325+ snprintf(thread, sizeof(thread), " thread: 0x%llx\n", 326+ record.thread); 327+ description_.append(thread); 328+ } 329+ break; 330+ case 6: 331+ if (record.dialog_mode) { 332+ char dialog_mode[128]; 333+ snprintf(dialog_mode, sizeof(dialog_mode), " dialog_mode: 0x%x\n", 334+ record.dialog_mode); 335+ description_.append(dialog_mode); 336+ } 337+ break; 338+ case 7: 339+ if (record.abort_cause) { 340+ char abort_cause[128]; 341+ snprintf(abort_cause, sizeof(abort_cause), " abort_cause: %lld\n", 342+ record.abort_cause); 343+ description_.append(abort_cause); 344+ } 345+ break; 346+ default: 347+ break; 348+ } 349+ } 350+ 351+ return true; 352+} 353+ 354+bool MinidumpMacCrashInfo::Read(uint32_t expected_size) { 355+ description_.clear(); 356+ records_.clear(); 357+ valid_ = false; 358+ 359+ MDRawMacCrashInfo crash_info; 360+ if (expected_size != sizeof(crash_info)) { 361+ BPLOG(ERROR) << "MinidumpMacCrashInfo size mismatch, " << 362+ expected_size << " != " << sizeof(crash_info); 363+ return false; 364+ } 365+ if (!minidump_->ReadBytes(&crash_info, sizeof(crash_info))) { 366+ BPLOG(ERROR) << "MinidumpMacCrashInfo could not read " << 367+ sizeof(crash_info) << " bytes"; 368+ return false; 369+ } 370+ if (minidump_->swap()) { 371+ Swap(&crash_info.stream_type); 372+ Swap(&crash_info.record_count); 373+ Swap(&crash_info.record_start_size); 374+ for (uint32_t i = 0; i < crash_info.record_count; ++i) { 375+ Swap(&crash_info.records[i].data_size); 376+ Swap(&crash_info.records[i].rva); 377+ } 378+ } 379+ if (crash_info.stream_type != MOZ_MACOS_CRASH_INFO_STREAM) { 380+ BPLOG(ERROR) << "MinidumpMacCrashInfo stream type mismatch, " << 381+ crash_info.stream_type << " != " << 382+ MOZ_MACOS_CRASH_INFO_STREAM; 383+ return false; 384+ } 385+ 386+ for (uint32_t i = 0; i < crash_info.record_count; ++i) { 387+ if (!ReadCrashInfoRecord(crash_info.records[i], 388+ crash_info.record_start_size)) { 389+ return false; 390+ } 391+ } 392+ 393+ valid_ = true; 394+ return true; 395+} 396+ 397+void MinidumpMacCrashInfo::Print() { 398+ if (!valid_) { 399+ BPLOG(ERROR) << "MinidumpMacCrashInfo cannot print invalid data"; 400+ return; 401+ } 402+ 403+ printf("MinidumpMacCrashInfo:\n\n"); 404+ printf("%s", description_.c_str()); 405+} 406 407 // 408 // Minidump 409@@ -5378,7 +5602,8 @@ bool Minidump::Read() { 410 case MD_SYSTEM_INFO_STREAM: 411 case MD_MISC_INFO_STREAM: 412 case MD_BREAKPAD_INFO_STREAM: 413- case MD_CRASHPAD_INFO_STREAM: { 414+ case MD_CRASHPAD_INFO_STREAM: 415+ case MOZ_MACOS_CRASH_INFO_STREAM: { 416 if (stream_map_->find(stream_type) != stream_map_->end()) { 417 // Another stream with this type was already found. A minidump 418 // file should contain at most one of each of these stream types. 419@@ -5499,6 +5724,11 @@ MinidumpCrashpadInfo* Minidump::GetCrash 420 return GetStream(&crashpad_info); 421 } 422 423+MinidumpMacCrashInfo* Minidump::GetMacCrashInfo() { 424+ MinidumpMacCrashInfo* mac_crash_info; 425+ return GetStream(&mac_crash_info); 426+} 427+ 428 static const char* get_stream_name(uint32_t stream_type) { 429 switch (stream_type) { 430 case MD_UNUSED_STREAM: 431@@ -5571,6 +5801,8 @@ static const char* get_stream_name(uint3 432 return "MD_LINUX_DSO_DEBUG"; 433 case MD_CRASHPAD_INFO_STREAM: 434 return "MD_CRASHPAD_INFO_STREAM"; 435+ case MOZ_MACOS_CRASH_INFO_STREAM: 436+ return "MOZ_MACOS_CRASH_INFO_STREAM"; 437 default: 438 return "unknown"; 439 } 440diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc 441--- a/src/processor/minidump_processor.cc 442+++ b/src/processor/minidump_processor.cc 443@@ -137,6 +137,12 @@ ProcessResult MinidumpProcessor::Process 444 } 445 } 446 447+ MinidumpMacCrashInfo *crash_info = dump->GetMacCrashInfo(); 448+ if (crash_info) { 449+ process_state->mac_crash_info_ = crash_info->description(); 450+ process_state->mac_crash_info_records_ = crash_info->records(); 451+ } 452+ 453 // This will just return an empty string if it doesn't exist. 454 process_state->assertion_ = GetAssertion(dump); 455 456diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc 457--- a/src/processor/stackwalk_common.cc 458+++ b/src/processor/stackwalk_common.cc 459@@ -872,6 +872,12 @@ void PrintProcessState(const ProcessStat 460 printf("Process uptime: not available\n"); 461 } 462 463+ if (!process_state.mac_crash_info().empty()) { 464+ printf("\n"); 465+ printf("Application-specific information:\n"); 466+ printf("%s", process_state.mac_crash_info().c_str()); 467+ } 468+ 469 // If the thread that requested the dump is known, print it first. 470 int requesting_thread = process_state.requesting_thread(); 471 if (requesting_thread != -1) { 472@@ -955,6 +961,44 @@ void PrintProcessStateMachineReadable(co 473 printf("\n"); 474 } 475 476+ const crash_info_record_t* crash_info_records = 477+ process_state.mac_crash_info_records(); 478+ size_t num_records = 479+ process_state.mac_crash_info_records_count(); 480+ for (size_t i = 0; i < num_records; ++i) { 481+ char thread_str[32]; 482+ if (crash_info_records[i].thread) { 483+ snprintf(thread_str, sizeof(thread_str), "0x%llx", 484+ crash_info_records[i].thread); 485+ } else { 486+ strncpy(thread_str, "0", sizeof(thread_str)); 487+ } 488+ char dialog_mode_str[32]; 489+ if (crash_info_records[i].dialog_mode) { 490+ snprintf(dialog_mode_str, sizeof(dialog_mode_str), "0x%x", 491+ crash_info_records[i].dialog_mode); 492+ } else { 493+ strncpy(dialog_mode_str, "0", sizeof(dialog_mode_str)); 494+ } 495+ char abort_cause_str[32]; 496+ if (crash_info_records[i].abort_cause) { 497+ snprintf(abort_cause_str, sizeof(abort_cause_str), "%lld", 498+ crash_info_records[i].abort_cause); 499+ } else { 500+ strncpy(abort_cause_str, "0", sizeof(abort_cause_str)); 501+ } 502+ printf("MacCrashInfo%c%s%c%lu%c%s%c%s%c%s%c%s%c%s%c%s%c%s\n", 503+ kOutputSeparator, crash_info_records[i].module_path.c_str(), 504+ kOutputSeparator, crash_info_records[i].version, 505+ kOutputSeparator, crash_info_records[i].message.c_str(), 506+ kOutputSeparator, crash_info_records[i].signature_string.c_str(), 507+ kOutputSeparator, crash_info_records[i].backtrace.c_str(), 508+ kOutputSeparator, crash_info_records[i].message2.c_str(), 509+ kOutputSeparator, thread_str, 510+ kOutputSeparator, dialog_mode_str, 511+ kOutputSeparator, abort_cause_str); 512+ } 513+ 514 PrintModulesMachineReadable(process_state.modules()); 515 PrintUnloadedModulesMachineReadable(process_state.unloaded_modules()); 516 517