ios: Set EXC_SOFT_SIGNAL for signal exceptions.

Right now there's no clear way to differentiate between Signal and Mach
exceptions. Instead, set EXC_SOFT_SIGNAL as the top level exception for
signal exceptions, moving the signal number to ExceptionInfo() and
and the signal code plus the two previous values into Codes().

Fixed:crashpad:389
Change-Id: Ia57f402b98be2a648febb58b9dee0cb80d9e5954
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3388874
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
diff --git a/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc
index c4d99e7..a45d4cc 100644
--- a/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc
+++ b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc
@@ -131,10 +131,16 @@
 #endif
   }
 
-  GetDataValueFromMap(exception_data, Key::kSignalNumber, &exception_);
-  GetDataValueFromMap(exception_data, Key::kSignalCode, &exception_info_);
+  exception_ = EXC_SOFT_SIGNAL;
+  GetDataValueFromMap(exception_data, Key::kSignalNumber, &exception_info_);
   GetDataValueFromMap(exception_data, Key::kSignalAddress, &exception_address_);
 
+  codes_.push_back(exception_);
+  codes_.push_back(exception_info_);
+  uint32_t code;
+  GetDataValueFromMap(exception_data, Key::kSignalCode, &code);
+  codes_.push_back(code);
+
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
 }
diff --git a/test/ios/crash_type_xctest.mm b/test/ios/crash_type_xctest.mm
index 2dcfac7..3613e22 100644
--- a/test/ios/crash_type_xctest.mm
+++ b/test/ios/crash_type_xctest.mm
@@ -65,7 +65,7 @@
   XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
 }
 
-- (void)verifyCrashReportException:(int)exception {
+- (void)verifyCrashReportException:(uint32_t)exception {
   // Confirm the app is not running.
   XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);
   XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);
@@ -75,7 +75,9 @@
   XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
   rootObject_ = [EDOClientService rootObjectWithPort:12345];
   XCTAssertEqual([rootObject_ pendingReportCount], 1);
-  XCTAssertEqual([rootObject_ pendingReportException], exception);
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportException:&report_exception]);
+  XCTAssertEqual(report_exception.unsignedIntValue, exception);
 }
 
 - (void)testEDO {
@@ -85,44 +87,61 @@
 
 - (void)testSegv {
   [rootObject_ crashSegv];
-#if defined(NDEBUG) && TARGET_OS_SIMULATOR
-  [self verifyCrashReportException:SIGINT];
+#if defined(NDEBUG)
+#if TARGET_OS_SIMULATOR
+  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];
 #else
-  [self verifyCrashReportException:SIGHUP];
+  [self verifyCrashReportException:EXC_BREAKPOINT];
+#endif
+#else
+  [self verifyCrashReportException:EXC_BAD_ACCESS];
 #endif
 }
 
 - (void)testKillAbort {
   [rootObject_ crashKillAbort];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
 }
 
 - (void)testTrap {
   [rootObject_ crashTrap];
 #if TARGET_OS_SIMULATOR
-  [self verifyCrashReportException:SIGINT];
+  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];
 #else
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_BREAKPOINT];
 #endif
 }
 
 - (void)testAbort {
   [rootObject_ crashAbort];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
 }
 
 - (void)testBadAccess {
   [rootObject_ crashBadAccess];
-#if defined(NDEBUG) && TARGET_OS_SIMULATOR
-  [self verifyCrashReportException:SIGINT];
+#if defined(NDEBUG)
+#if TARGET_OS_SIMULATOR
+  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];
 #else
-  [self verifyCrashReportException:SIGHUP];
+  [self verifyCrashReportException:EXC_BREAKPOINT];
+#endif
+#else
+  [self verifyCrashReportException:EXC_BAD_ACCESS];
 #endif
 }
 
 - (void)testException {
   [rootObject_ crashException];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
 }
 
 - (void)testNSException {
@@ -183,7 +202,7 @@
 
 - (void)testRecursion {
   [rootObject_ crashRecursion];
-  [self verifyCrashReportException:SIGHUP];
+  [self verifyCrashReportException:EXC_BAD_ACCESS];
 }
 
 - (void)testClientAnnotations {
@@ -192,7 +211,11 @@
   // Set app launch args to trigger different client annotations.
   NSArray<NSString*>* old_args = app_.launchArguments;
   app_.launchArguments = @[ @"--alternate-client-annotations" ];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
+
   app_.launchArguments = old_args;
 
   // Confirm the initial crash took the standard annotations.
@@ -205,7 +228,10 @@
   // Confirm passing alternate client annotation args works.
   [rootObject_ clearPendingReports];
   [rootObject_ crashKillAbort];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
+
   dict = [rootObject_ getProcessAnnotations];
   XCTAssertTrue([dict[@"crashpad"] isEqualToString:@"no"]);
   XCTAssertTrue([dict[@"plat"] isEqualToString:@"macOS"]);
@@ -220,7 +246,7 @@
     return;
   }
   [rootObject_ crashWithCrashInfoMessage];
-  [self verifyCrashReportException:SIGHUP];
+  [self verifyCrashReportException:EXC_BAD_ACCESS];
   NSDictionary* dict = [rootObject_ getAnnotations];
   NSString* dyldMessage = dict[@"vector"][0];
   XCTAssertTrue([dyldMessage isEqualToString:@"dyld: in dlsym()"]);
@@ -235,7 +261,7 @@
     return;
   }
   [rootObject_ crashWithDyldErrorString];
-  [self verifyCrashReportException:SIGINT];
+  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];
   NSArray* vector = [rootObject_ getAnnotations][@"vector"];
   // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp
   // ImageLoaderMachO::doInitialization().
@@ -246,7 +272,11 @@
 
 - (void)testCrashWithAnnotations {
   [rootObject_ crashWithAnnotations];
-  [self verifyCrashReportException:SIGABRT];
+  [self verifyCrashReportException:EXC_SOFT_SIGNAL];
+  NSNumber* report_exception;
+  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);
+  XCTAssertEqual(report_exception.intValue, SIGABRT);
+
   NSDictionary* dict = [rootObject_ getAnnotations];
   NSDictionary* simpleMap = dict[@"simplemap"];
   XCTAssertTrue([simpleMap[@"#TEST# empty_value"] isEqualToString:@""]);
diff --git a/test/ios/host/cptest_application_delegate.mm b/test/ios/host/cptest_application_delegate.mm
index 44a7d88..3bf39df 100644
--- a/test/ios/host/cptest_application_delegate.mm
+++ b/test/ios/host/cptest_application_delegate.mm
@@ -68,6 +68,24 @@
   return database->GetPendingReports(pending_reports);
 }
 
+std::unique_ptr<crashpad::ProcessSnapshotMinidump>
+GetProcessSnapshotMinidumpFromSinglePending() {
+  std::vector<Report> pending_reports;
+  OperationStatus status = GetPendingReports(&pending_reports);
+  if (status != crashpad::CrashReportDatabase::kNoError ||
+      pending_reports.size() != 1) {
+    return nullptr;
+  }
+
+  auto reader = std::make_unique<crashpad::FileReader>();
+  auto process_snapshot = std::make_unique<crashpad::ProcessSnapshotMinidump>();
+  if (!reader->Open(pending_reports[0].file_path) ||
+      !process_snapshot->Initialize(reader.get())) {
+    return nullptr;
+  }
+  return process_snapshot;
+}
+
 [[clang::optnone]] void recurse(int counter) {
   // Fill up the stack faster.
   int arr[1024];
@@ -159,40 +177,36 @@
   return pending_reports.size();
 }
 
-- (int)pendingReportException {
-  std::vector<Report> pending_reports;
-  OperationStatus status = GetPendingReports(&pending_reports);
-  if (status != crashpad::CrashReportDatabase::kNoError ||
-      pending_reports.size() != 1) {
-    return -1;
-  }
+- (bool)pendingReportException:(NSNumber**)exception {
+  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
+  if (!process_snapshot || !process_snapshot->Exception()->Exception())
+    return false;
+  *exception = [NSNumber
+      numberWithUnsignedInt:process_snapshot->Exception()->Exception()];
+  return true;
+}
 
-  auto reader = std::make_unique<crashpad::FileReader>();
-  reader->Open(pending_reports[0].file_path);
-  crashpad::ProcessSnapshotMinidump process_snapshot;
-  process_snapshot.Initialize(reader.get());
-  return static_cast<int>(process_snapshot.Exception()->Exception());
+- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info {
+  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
+  if (!process_snapshot || !process_snapshot->Exception()->ExceptionInfo())
+    return false;
+
+  *exception_info = [NSNumber
+      numberWithUnsignedInt:process_snapshot->Exception()->ExceptionInfo()];
+  return true;
 }
 
 - (NSDictionary*)getAnnotations {
-  std::vector<Report> pending_reports;
-  OperationStatus status = GetPendingReports(&pending_reports);
-  if (status != crashpad::CrashReportDatabase::kNoError ||
-      pending_reports.size() != 1) {
+  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
+  if (!process_snapshot)
     return @{};
-  }
-
-  auto reader = std::make_unique<crashpad::FileReader>();
-  reader->Open(pending_reports[0].file_path);
-  crashpad::ProcessSnapshotMinidump process_snapshot;
-  process_snapshot.Initialize(reader.get());
 
   NSDictionary* dict = @{
     @"simplemap" : [@{} mutableCopy],
     @"vector" : [@[] mutableCopy],
     @"objects" : [@[] mutableCopy]
   };
-  for (const auto* module : process_snapshot.Modules()) {
+  for (const auto* module : process_snapshot->Modules()) {
     for (const auto& kv : module->AnnotationsSimpleMap()) {
       [dict[@"simplemap"] setValue:@(kv.second.c_str())
                             forKey:@(kv.first.c_str())];
@@ -215,19 +229,12 @@
 }
 
 - (NSDictionary*)getProcessAnnotations {
-  std::vector<Report> pending_reports;
-  OperationStatus status = GetPendingReports(&pending_reports);
-  if (status != crashpad::CrashReportDatabase::kNoError ||
-      pending_reports.size() != 1) {
+  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
+  if (!process_snapshot)
     return @{};
-  }
 
-  auto reader = std::make_unique<crashpad::FileReader>();
-  reader->Open(pending_reports[0].file_path);
-  crashpad::ProcessSnapshotMinidump process_snapshot;
-  process_snapshot.Initialize(reader.get());
   NSDictionary* dict = [@{} mutableCopy];
-  for (const auto& kv : process_snapshot.AnnotationsSimpleMap()) {
+  for (const auto& kv : process_snapshot->AnnotationsSimpleMap()) {
     [dict setValue:@(kv.second.c_str()) forKey:@(kv.first.c_str())];
   }
 
diff --git a/test/ios/host/cptest_shared_object.h b/test/ios/host/cptest_shared_object.h
index bcd7554..90e3de8 100644
--- a/test/ios/host/cptest_shared_object.h
+++ b/test/ios/host/cptest_shared_object.h
@@ -32,9 +32,15 @@
 // report.
 - (int)pendingReportCount;
 
-// Returns exception code when there's a single pending report, or -1 if there's
-// a different number of pending reports.
-- (int)pendingReportException;
+// Returns true if there's a single pending report and sets the exception code
+// in the out |exception| parameter. Returns false if there's a different number
+// of pending reports.
+- (bool)pendingReportException:(NSNumber**)exception;
+
+// Returns true if there's a single pending report and sets the second-level
+// exception code in the out |exception_info| parameter. Returns false if
+// there's a different number of pending reports.
+- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info;
 
 // Return an NSDictionary with a dictionary named "simplemap", an array named
 // "vector" and an array named "objects", representing the combination of all