OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/chrome_browser_application_mac.h" | 5 #import "chrome/browser/mac/exception_processor.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <sys/wait.h> |
9 | 10 |
10 #include <memory> | 11 #include "base/mac/os_crash_dumps.h" |
11 | |
12 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
13 #include "base/metrics/histogram_samples.h" | 13 #include "base/metrics/histogram_samples.h" |
14 #include "base/metrics/statistics_recorder.h" | 14 #include "base/metrics/statistics_recorder.h" |
15 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
16 | 16 |
17 using base::HistogramBase; | 17 using base::HistogramBase; |
18 using base::HistogramSamples; | 18 using base::HistogramSamples; |
19 using base::StatisticsRecorder; | 19 using base::StatisticsRecorder; |
20 | 20 |
21 namespace chrome_browser_application_mac { | 21 namespace chrome { |
22 | 22 |
23 // Generate an NSException with the given name. | 23 // Generate an NSException with the given name. |
24 NSException* ExceptionNamed(NSString* name) { | 24 NSException* ExceptionNamed(NSString* name) { |
25 return [NSException exceptionWithName:name | 25 return [NSException exceptionWithName:name |
26 reason:@"No reason given" | 26 reason:@"No reason given" |
27 userInfo:nil]; | 27 userInfo:nil]; |
28 } | 28 } |
29 | 29 |
30 // Helper to keep binning expectations readible. | 30 // Helper to keep binning expectations readible. |
31 size_t BinForExceptionNamed(NSString* name) { | 31 size_t BinForExceptionNamed(NSString* name) { |
32 return BinForException(ExceptionNamed(name)); | 32 return BinForException(ExceptionNamed(name)); |
33 } | 33 } |
34 | 34 |
35 TEST(ChromeApplicationMacTest, ExceptionBinning) { | 35 TEST(ExceptionProcessorTest, ExceptionBinning) { |
36 // These exceptions must be in this order. | 36 // These exceptions must be in this order. |
37 EXPECT_EQ(BinForExceptionNamed(NSGenericException), 0U); | 37 EXPECT_EQ(BinForExceptionNamed(NSGenericException), 0U); |
38 EXPECT_EQ(BinForExceptionNamed(NSRangeException), 1U); | 38 EXPECT_EQ(BinForExceptionNamed(NSRangeException), 1U); |
39 EXPECT_EQ(BinForExceptionNamed(NSInvalidArgumentException), 2U); | 39 EXPECT_EQ(BinForExceptionNamed(NSInvalidArgumentException), 2U); |
40 EXPECT_EQ(BinForExceptionNamed(NSMallocException), 3U); | 40 EXPECT_EQ(BinForExceptionNamed(NSMallocException), 3U); |
41 | 41 |
42 // Random other exceptions map to |kUnknownNSException|. | 42 // Random other exceptions map to |kUnknownNSException|. |
43 EXPECT_EQ(BinForExceptionNamed(@"CustomName"), kUnknownNSException); | 43 EXPECT_EQ(BinForExceptionNamed(@"CustomName"), kUnknownNSException); |
44 EXPECT_EQ(BinForExceptionNamed(@"Custom Name"), kUnknownNSException); | 44 EXPECT_EQ(BinForExceptionNamed(@"Custom Name"), kUnknownNSException); |
45 EXPECT_EQ(BinForExceptionNamed(@""), kUnknownNSException); | 45 EXPECT_EQ(BinForExceptionNamed(@""), kUnknownNSException); |
46 EXPECT_EQ(BinForException(nil), kUnknownNSException); | 46 EXPECT_EQ(BinForException(nil), kUnknownNSException); |
47 } | 47 } |
48 | 48 |
49 TEST(ChromeApplicationMacTest, RecordException) { | 49 TEST(ExceptionProcessorTest, RecordException) { |
50 // Start up a histogram recorder. | 50 // Start up a histogram recorder. |
51 // TODO(rtenneti): Leaks StatisticsRecorder and will update suppressions. | 51 // TODO(rtenneti): Leaks StatisticsRecorder and will update suppressions. |
52 base::StatisticsRecorder::Initialize(); | 52 base::StatisticsRecorder::Initialize(); |
53 | 53 |
54 StatisticsRecorder::Histograms histograms; | 54 StatisticsRecorder::Histograms histograms; |
55 StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms); | 55 StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms); |
56 EXPECT_EQ(0U, histograms.size()); | 56 EXPECT_EQ(0U, histograms.size()); |
57 | 57 |
58 // Record some known exceptions. | 58 // Record some known exceptions. |
59 RecordExceptionWithUma(ExceptionNamed(NSGenericException)); | 59 RecordExceptionWithUma(ExceptionNamed(NSGenericException)); |
(...skipping 24 matching lines...) Expand all Loading... |
84 EXPECT_EQ(3, samples->GetCount(2)); | 84 EXPECT_EQ(3, samples->GetCount(2)); |
85 EXPECT_EQ(2, samples->GetCount(3)); | 85 EXPECT_EQ(2, samples->GetCount(3)); |
86 | 86 |
87 // The unknown exceptions should end up in the overflow bucket. | 87 // The unknown exceptions should end up in the overflow bucket. |
88 EXPECT_TRUE(histograms[0]->HasConstructionArguments(1, | 88 EXPECT_TRUE(histograms[0]->HasConstructionArguments(1, |
89 kUnknownNSException, | 89 kUnknownNSException, |
90 kUnknownNSException + 1)); | 90 kUnknownNSException + 1)); |
91 EXPECT_EQ(4, samples->GetCount(kUnknownNSException)); | 91 EXPECT_EQ(4, samples->GetCount(kUnknownNSException)); |
92 } | 92 } |
93 | 93 |
94 } // chrome_browser_application_mac | 94 void RaiseExceptionInRunLoop() { |
| 95 CFRunLoopRef run_loop = CFRunLoopGetCurrent(); |
| 96 |
| 97 CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{ |
| 98 [NSException raise:@"ThrowExceptionInRunLoop" format:nil]; |
| 99 }); |
| 100 CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{ |
| 101 CFRunLoopStop(run_loop); |
| 102 }); |
| 103 CFRunLoopRun(); |
| 104 } |
| 105 |
| 106 void ThrowExceptionInRunLoop() { |
| 107 base::mac::DisableOSCrashDumps(); |
| 108 chrome::InstallObjcExceptionPreprocessor(); |
| 109 |
| 110 RaiseExceptionInRunLoop(); |
| 111 |
| 112 fprintf(stderr, "TEST FAILED\n"); |
| 113 exit(1); |
| 114 } |
| 115 |
| 116 // Tests that when the preprocessor is installed, exceptions thrown from |
| 117 // a runloop callout are made fatal, so that the stack trace is useful. |
| 118 TEST(ExceptionProcessorTest, ThrowExceptionInRunLoop) { |
| 119 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| 120 EXPECT_DEATH(ThrowExceptionInRunLoop(), |
| 121 ".*FATAL:exception_processor\\.mm.*" |
| 122 "Terminating from Objective-C exception:.*"); |
| 123 } |
| 124 |
| 125 void ThrowAndCatchExceptionInRunLoop() { |
| 126 base::mac::DisableOSCrashDumps(); |
| 127 chrome::InstallObjcExceptionPreprocessor(); |
| 128 |
| 129 CFRunLoopRef run_loop = CFRunLoopGetCurrent(); |
| 130 CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{ |
| 131 @try { |
| 132 [NSException raise:@"ObjcExceptionPreprocessCaught" format:nil]; |
| 133 } @catch (id exception) { |
| 134 } |
| 135 }); |
| 136 |
| 137 CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{ |
| 138 CFRunLoopStop(run_loop); |
| 139 }); |
| 140 |
| 141 CFRunLoopRun(); |
| 142 |
| 143 fprintf(stderr, "TEST PASS\n"); |
| 144 exit(0); |
| 145 } |
| 146 |
| 147 // Tests that exceptions can still be caught when the preprocessor is enabled. |
| 148 TEST(ExceptionProcessorTest, ThrowAndCatchExceptionInRunLoop) { |
| 149 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| 150 EXPECT_EXIT(ThrowAndCatchExceptionInRunLoop(), |
| 151 [](int exit_code) -> bool { |
| 152 return WEXITSTATUS(exit_code) == 0; |
| 153 }, |
| 154 ".*TEST PASS.*"); |
| 155 } |
| 156 |
| 157 void ThrowExceptionInRunLoopWithoutProcessor() { |
| 158 base::mac::DisableOSCrashDumps(); |
| 159 chrome::UninstallObjcExceptionPreprocessor(); |
| 160 |
| 161 @try { |
| 162 RaiseExceptionInRunLoop(); |
| 163 } @catch (id exception) { |
| 164 fprintf(stderr, "TEST PASS\n"); |
| 165 exit(0); |
| 166 } |
| 167 |
| 168 fprintf(stderr, "TEST FAILED\n"); |
| 169 exit(1); |
| 170 } |
| 171 |
| 172 // Tests basic exception handling when the preprocessor is disabled. |
| 173 TEST(ExceptionProcessorTest, ThrowExceptionInRunLoopWithoutProcessor) { |
| 174 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| 175 EXPECT_EXIT(ThrowExceptionInRunLoopWithoutProcessor(), |
| 176 [](int exit_code) -> bool { |
| 177 return WEXITSTATUS(exit_code) == 0; |
| 178 }, |
| 179 ".*TEST PASS.*"); |
| 180 } |
| 181 |
| 182 } // namespace chrome |
OLD | NEW |