diff options
author | nileshagrawal@chromium.org <nileshagrawal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-09 16:59:12 +0000 |
---|---|---|
committer | nileshagrawal@chromium.org <nileshagrawal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-09 16:59:12 +0000 |
commit | 2037ef804dac0e366e751d30aecfe1f868fb4f2b (patch) | |
tree | 01093f4b02edb7b2d979f58f1a7f0dc241ab70f1 | |
parent | cc3a5c8ca42579c0f9f710c18f51c566b4c75e86 (diff) | |
download | chromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.zip chromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.tar.gz chromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.tar.bz2 |
Detect crashes while running native tests in APK.
Adding signal handlers (for fatal signals) to output a marker indicating
that the test crashed.
BUG=125059
TEST=
Review URL: https://chromiumcodereview.appspot.com/10310046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136050 0039d316-1c4b-4281-b951-d872f2087c98
-rwxr-xr-x | build/android/run_tests.py | 3 | ||||
-rw-r--r-- | build/android/single_test_runner.py | 7 | ||||
-rw-r--r-- | build/android/test_package.py | 22 | ||||
-rw-r--r-- | build/android/test_result.py | 8 | ||||
-rw-r--r-- | testing/android/native_test_launcher.cc | 41 |
5 files changed, 67 insertions, 14 deletions
diff --git a/build/android/run_tests.py b/build/android/run_tests.py index 1015d26..ca1f877 100755 --- a/build/android/run_tests.py +++ b/build/android/run_tests.py @@ -193,8 +193,7 @@ def RunTests(device, test_suite, gtest_filter, test_arguments, rebaseline, if test_suite in _TEST_SUITES: logging.critical('(Remember to include the path: out/Release/%s)', test_suite) - return TestResults.FromOkAndFailed([], [BaseTestResult(test_suite, '')], - False, False) + return TestResults.FromRun(failed=[BaseTestResult(test_suite, '')]) fully_qualified_test_suites = [test_suite] else: fully_qualified_test_suites = FullyQualifiedTestSuites(apk) diff --git a/build/android/single_test_runner.py b/build/android/single_test_runner.py index fe59cb6..503088c 100644 --- a/build/android/single_test_runner.py +++ b/build/android/single_test_runner.py @@ -271,10 +271,9 @@ class SingleTestRunner(BaseTestRunner): logging.info('*' * 80) if executed_names == all_tests: break - self.test_results = TestResults.FromOkAndFailed(list(executed_results - - failed_results), - list(failed_results), - False, False) + self.test_results = TestResults.FromRun( + ok=list(executed_results - failed_results), + failed=list(failed_results)) def RunTests(self): """Runs all tests (in rebaseline mode, runs each test in isolation). diff --git a/build/android/test_package.py b/build/android/test_package.py index e4b10e1..8364c94 100644 --- a/build/android/test_package.py +++ b/build/android/test_package.py @@ -129,11 +129,15 @@ class TestPackage(object): """ ok_tests = [] failed_tests = [] + crashed_tests = [] timed_out = False overall_fail = False re_run = re.compile('\[ RUN \] ?(.*)\r\n') # APK tests rely on the END tag. re_end = re.compile('\[ END \] ?(.*)\r\n') + # Signal handlers are installed before starting tests + # to output the CRASHED marker when a crash happens. + re_crash = re.compile('\[ CRASHED \](.*)\r\n') re_fail = re.compile('\[ FAILED \] ?(.*)\r\n') re_runner_fail = re.compile('\[ RUNNER_FAILED \] ?(.*)\r\n') re_ok = re.compile('\[ OK \] ?(.*)\r\n') @@ -152,16 +156,22 @@ class TestPackage(object): if self.dump_debug_info: self.dump_debug_info.TakeScreenshot('_Test_Start_Run_') full_test_name = p.match.group(1) - found = p.expect([re_ok, re_fail, pexpect.EOF, pexpect.TIMEOUT], + found = p.expect([re_ok, re_fail, re_crash, pexpect.EOF, pexpect.TIMEOUT], timeout=self.timeout) if found == 0: # re_ok ok_tests += [BaseTestResult(full_test_name.replace('\r', ''), p.before)] continue + if found == 2: # re_crash + crashed_tests += [BaseTestResult(full_test_name.replace('\r', ''), + p.before)] + overall_fail = True + break + # The test failed. failed_tests += [BaseTestResult(full_test_name.replace('\r', ''), p.before)] - if found >= 2: - # The test crashed / bailed out (i.e., didn't print OK or FAIL). + if found >= 3: + # The test bailed out (i.e., didn't print OK or FAIL). if found == 3: # pexpect.TIMEOUT logging.error('Test terminated after %d second timeout.', self.timeout) @@ -177,5 +187,7 @@ class TestPackage(object): '\npexpect.after: %s' % (p.before, p.after))] - return TestResults.FromOkAndFailed(ok_tests, failed_tests, - timed_out, overall_fail) + # Create TestResults and return + return TestResults.FromRun(ok=ok_tests, failed=failed_tests, + crashed=crashed_tests, timed_out=timed_out, + overall_fail=overall_fail) diff --git a/build/android/test_result.py b/build/android/test_result.py index dfafca7..7d9b216 100644 --- a/build/android/test_result.py +++ b/build/android/test_result.py @@ -61,10 +61,12 @@ class TestResults(object): self.overall_fail = False @staticmethod - def FromOkAndFailed(ok, failed, timed_out, overall_fail): + def FromRun(ok=None, failed=None, crashed=None, timed_out=False, + overall_fail=False): ret = TestResults() - ret.ok = ok - ret.failed = failed + ret.ok = ok or [] + ret.failed = failed or [] + ret.crashed = crashed or [] ret.timed_out = timed_out ret.overall_fail = overall_fail return ret diff --git a/testing/android/native_test_launcher.cc b/testing/android/native_test_launcher.cc index 5e600b8..ad1db27 100644 --- a/testing/android/native_test_launcher.cc +++ b/testing/android/native_test_launcher.cc @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This class sets up the environment for running the native tests inside an +// android application. It outputs (to logcat) markers identifying the +// START/END/CRASH of the test suite, FAILURE/SUCCESS of individual tests etc. +// These markers are read by the test runner script to generate test results. +// It injects an event listener in gtest to detect various test stages and +// installs signal handlers to detect crashes. + +#include <android/log.h> +#include <signal.h> #include <stdio.h> #include "base/android/jni_android.h" @@ -26,6 +35,33 @@ extern int main(int argc, char** argv); namespace { +// The list of signals which are considered to be crashes. +const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 +}; + +struct sigaction g_old_sa[NSIG]; + +// This function runs in a compromised context. It should not allocate memory. +void SignalHandler(int sig, siginfo_t *info, void *reserved) +{ + // Output the crash marker. + __android_log_write(ANDROID_LOG_ERROR, "chromium", "[ CRASHED ]"); + g_old_sa[sig].sa_sigaction(sig, info, reserved); +} + +void InstallHandlers() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_SIGINFO; + + for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) { + sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]); + } +} + void ParseArgsFromString(const std::string& command_line, std::vector<std::string>* args) { StringTokenizer tokenizer(command_line, kWhitespaceASCII); @@ -191,11 +227,16 @@ static void RunTests(JNIEnv* env, // This is called by the VM when the shared library is first loaded. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + + // Install signal handlers to detect crashes. + InstallHandlers(); + base::android::InitVM(vm); JNIEnv* env = base::android::AttachCurrentThread(); if (!RegisterNativesImpl(env)) { return -1; } LibraryLoadedOnMainThread(env); + return JNI_VERSION_1_4; } |