diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 23:10:07 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 23:10:07 +0000 |
commit | 8cfc21f751e337584e2b60f2401e90a127fcc24e (patch) | |
tree | e25f87c6660887cc32d6a930cb81a1c65edee2a0 | |
parent | 1deca10016c82cfb473e54d5511c9215928b14eb (diff) | |
download | chromium_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.h | 16 | ||||
-rw-r--r-- | chrome/browser/chrome_application_mac.mm | 72 | ||||
-rw-r--r-- | chrome/browser/chrome_application_mac_unittest.mm | 81 | ||||
-rwxr-xr-x | chrome/chrome.gyp | 1 |
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', |