summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 23:10:07 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 23:10:07 +0000
commit8cfc21f751e337584e2b60f2401e90a127fcc24e (patch)
treee25f87c6660887cc32d6a930cb81a1c65edee2a0
parent1deca10016c82cfb473e54d5511c9215928b14eb (diff)
downloadchromium_src-8cfc21f751e337584e2b60f2401e90a127fcc24e.zip
chromium_src-8cfc21f751e337584e2b60f2401e90a127fcc24e.tar.gz
chromium_src-8cfc21f751e337584e2b60f2401e90a127fcc24e.tar.bz2
[Mac] UMA histogram for uncaught NSExceptions.
NSExceptions break C++ assumptions. This starts to track how often NSExceptions happen. http://crbug.com/24463 TEST=Brower continues to operate. Review URL: http://codereview.chromium.org/264061 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29204 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chrome_application_mac.h16
-rw-r--r--chrome/browser/chrome_application_mac.mm72
-rw-r--r--chrome/browser/chrome_application_mac_unittest.mm81
-rwxr-xr-xchrome/chrome.gyp1
4 files changed, 170 insertions, 0 deletions
diff --git a/chrome/browser/chrome_application_mac.h b/chrome/browser/chrome_application_mac.h
index 6d5d95f..60b6438 100644
--- a/chrome/browser/chrome_application_mac.h
+++ b/chrome/browser/chrome_application_mac.h
@@ -12,6 +12,22 @@
@interface CrApplication : NSApplication
@end
+// Namespace for exception-reporting helper functions. Exposed for
+// testing purposes.
+namespace CrApplicationNSException {
+
+// Bin for unknown exceptions.
+extern const size_t kUnknownNSException;
+
+// Returns the histogram bin for |exception| if it is one we track
+// specifically, or |kUnknownNSException| if unknown.
+size_t BinForException(NSException* exception);
+
+// Use UMA to track exception occurance.
+void RecordExceptionWithUma(NSException* exception);
+
+} // CrApplicationNSException
+
#endif // __OBJC__
// CrApplicationCC provides access to CrApplication Objective-C selectors from
diff --git a/chrome/browser/chrome_application_mac.mm b/chrome/browser/chrome_application_mac.mm
index 2457dc2..86c6c4a 100644
--- a/chrome/browser/chrome_application_mac.mm
+++ b/chrome/browser/chrome_application_mac.mm
@@ -4,9 +4,63 @@
#import "chrome/browser/chrome_application_mac.h"
+#import "base/histogram.h"
+#import "base/logging.h"
#import "base/scoped_nsobject.h"
#import "chrome/app/breakpad_mac.h"
+namespace CrApplicationNSException {
+
+// Maximum number of known named exceptions we'll support. There is
+// no central registration, but I only find about 75 possibilities in
+// the system frameworks, and many of them are probably not
+// interesting to track in aggregate (those relating to distributed
+// objects, for instance).
+const size_t kKnownNSExceptionCount = 25;
+
+const size_t kUnknownNSException = kKnownNSExceptionCount;
+
+size_t BinForException(NSException* exception) {
+ // A list of common known exceptions. The list position will
+ // determine where they live in the histogram, so never move them
+ // around, only add to the end.
+ static const NSString* kKnownNSExceptionNames[] = {
+ // ???
+ NSGenericException,
+
+ // Out-of-range on NSString or NSArray.
+ NSRangeException,
+
+ // Invalid arg to method, unrecognized selector.
+ NSInvalidArgumentException,
+
+ // malloc() returned null in object creation, I think.
+ NSMallocException,
+
+ nil
+ };
+
+ // Make sure our array hasn't outgrown our abilities to track it.
+ DCHECK_LE(arraysize(kKnownNSExceptionNames), kKnownNSExceptionCount);
+
+ const NSString* name = [exception name];
+ for (int i = 0; kKnownNSExceptionNames[i]; ++i) {
+ if (name == kKnownNSExceptionNames[i]) {
+ return i;
+ }
+ }
+ return kUnknownNSException;
+}
+
+void RecordExceptionWithUma(NSException* exception) {
+ static LinearHistogram histogram("OSX.NSException", 0, kUnknownNSException,
+ kUnknownNSException + 1);
+ histogram.SetFlags(kUmaTargetedHistogramFlag);
+ histogram.Add(BinForException(exception));
+}
+
+} // CrApplicationNSException
+
namespace {
// Helper to make it easy to get crash keys right.
@@ -130,6 +184,24 @@ class ScopedCrashKey {
return [super sendAction:anAction to:aTarget from:sender];
}
+// NSExceptions which are caught by the event loop are logged here.
+// NSException uses setjmp/longjmp, which can be very bad for C++, so
+// we attempt to track and report them.
+- (void)reportException:(NSException *)anException {
+ // If we throw an exception in this code, we can create an infinite
+ // loop. If we throw out of the if() without resetting
+ // |reportException|, we'll stop reporting exceptions for this run.
+ static BOOL reportingException = NO;
+ DCHECK(!reportingException);
+ if (!reportingException) {
+ reportingException = YES;
+ CrApplicationNSException::RecordExceptionWithUma(anException);
+ reportingException = NO;
+ }
+
+ [super reportException:anException];
+}
+
@end
namespace CrApplicationCC {
diff --git a/chrome/browser/chrome_application_mac_unittest.mm b/chrome/browser/chrome_application_mac_unittest.mm
new file mode 100644
index 0000000..dbc1d90
--- /dev/null
+++ b/chrome/browser/chrome_application_mac_unittest.mm
@@ -0,0 +1,81 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/histogram.h"
+#import "chrome/browser/chrome_application_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace CrApplicationNSException {
+
+// Generate an NSException with the given name.
+NSException* ExceptionNamed(NSString* name) {
+ return [NSException exceptionWithName:name
+ reason:@"No reason given"
+ userInfo:nil];
+}
+
+// Helper to keep binning expectations readible.
+size_t BinForExceptionNamed(NSString* name) {
+ return BinForException(ExceptionNamed(name));
+}
+
+TEST(ChromeApplicationMacTest, ExceptionBinning) {
+ // These exceptions must be in this order.
+ EXPECT_EQ(BinForExceptionNamed(NSGenericException), 0U);
+ EXPECT_EQ(BinForExceptionNamed(NSRangeException), 1U);
+ EXPECT_EQ(BinForExceptionNamed(NSInvalidArgumentException), 2U);
+ EXPECT_EQ(BinForExceptionNamed(NSMallocException), 3U);
+
+ // Random other exceptions map to |kUnknownNSException|.
+ EXPECT_EQ(BinForExceptionNamed(@"CustomName"), kUnknownNSException);
+ EXPECT_EQ(BinForExceptionNamed(@"Custom Name"), kUnknownNSException);
+ EXPECT_EQ(BinForExceptionNamed(@""), kUnknownNSException);
+ EXPECT_EQ(BinForException(nil), kUnknownNSException);
+}
+
+TEST(ChromeApplicationMacTest, RecordException) {
+ // Start up a histogram recorder.
+ StatisticsRecorder recorder;
+
+ StatisticsRecorder::Histograms histograms;
+ StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
+ EXPECT_EQ(0U, histograms.size());
+
+ // Record some known exceptions.
+ RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+ RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+ RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+ RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+ RecordExceptionWithUma(ExceptionNamed(NSRangeException));
+ RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+ RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+ RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+ RecordExceptionWithUma(ExceptionNamed(NSMallocException));
+ RecordExceptionWithUma(ExceptionNamed(NSMallocException));
+
+ // Record some unknown exceptions.
+ RecordExceptionWithUma(ExceptionNamed(@"CustomName"));
+ RecordExceptionWithUma(ExceptionNamed(@"Custom Name"));
+ RecordExceptionWithUma(ExceptionNamed(@""));
+ RecordExceptionWithUma(nil);
+
+ // We should have exactly the right number of exceptions.
+ StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
+ EXPECT_EQ(1U, histograms.size());
+ EXPECT_EQ(kUmaTargetedHistogramFlag, histograms[0]->flags());
+ Histogram::SampleSet sample;
+ histograms[0]->SnapshotSample(&sample);
+ EXPECT_EQ(4, sample.counts(0));
+ EXPECT_EQ(1, sample.counts(1));
+ EXPECT_EQ(3, sample.counts(2));
+ EXPECT_EQ(2, sample.counts(3));
+
+ // The unknown exceptions should end up in the overflow bucket.
+ EXPECT_EQ(kUnknownNSException + 1, histograms[0]->bucket_count());
+ EXPECT_EQ(4, sample.counts(kUnknownNSException));
+}
+
+} // CrApplicationNSException
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 49fcb6c..508b7ee 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -4341,6 +4341,7 @@
'browser/browser_commands_unittest.cc',
'browser/browser_theme_provider_unittest.cc',
'browser/browser_unittest.cc',
+ 'browser/chrome_application_mac_unittest.mm',
'browser/debugger/devtools_remote_message_unittest.cc',
'browser/debugger/devtools_remote_listen_socket_unittest.cc',
'browser/debugger/devtools_remote_listen_socket_unittest.h',