def cleanupAndCheckForCrashes(self, browser_config, profile_dir, test_name): """cleanup browser processes and process crashes if found""" # cleanup processes self._ffprocess.cleanupProcesses(browser_config['process'], browser_config['child_process'], browser_config['browser_wait']) # find stackwalk binary if platform.system() in ('Windows', 'Microsoft'): stackwalkpaths = ['win32', 'minidump_stackwalk.exe'] elif platform.system() == 'Linux': # are we 64 bit? if '64' in platform.architecture()[0]: stackwalkpaths = ['linux64', 'minidump_stackwalk'] else: stackwalkpaths = ['linux', 'minidump_stackwalk'] elif platform.system() == 'Darwin': stackwalkpaths = ['osx', 'minidump_stackwalk'] else: # no minidump_stackwalk available for your platform return stackwalkbin = os.path.join(os.path.dirname(__file__), 'breakpad', *stackwalkpaths) assert os.path.exists(stackwalkbin), "minidump_stackwalk binary not found: %s" % stackwalkbin if browser_config['remote'] is True: # favour using Java exceptions in the logcat over minidumps if os.path.exists('logcat.log'): with open('logcat.log') as f: logcat = f.read().split('\r') found = mozcrash.check_for_java_exception(logcat) remoteminidumpdir = profile_dir + '/minidumps/' if not found: # check for minidumps minidumpdir = tempfile.mkdtemp() try: if self._ffprocess.testAgent.dirExists(remoteminidumpdir): self._ffprocess.testAgent.getDirectory(remoteminidumpdir, minidumpdir) except mozdevice.DMError: print "Remote Device Error: Error getting crash minidumps from device" raise found = mozcrash.check_for_crashes(minidumpdir, browser_config['symbols_path'], stackwalk_binary=stackwalkbin, test_name=test_name) self._hostproc.removeDirectory(minidumpdir) # cleanup dumps on remote self._ffprocess.testAgent.removeDir(remoteminidumpdir) else: # check for minidumps minidumpdir = os.path.join(profile_dir, 'minidumps') found = mozcrash.check_for_crashes(minidumpdir, browser_config['symbols_path'], stackwalk_binary=stackwalkbin, test_name=test_name) if found: raise talosCrash("Found crashes after test run, terminating test")
def checkForCrashes(self, directory, symbolsPath): crashed = False remote_dump_dir = self._remoteProfile + '/minidumps' print "checking for crashes in '%s'" % remote_dump_dir if self._devicemanager.dirExists(remote_dump_dir): local_dump_dir = tempfile.mkdtemp() self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) try: logger = get_default_logger() if logger is not None: crashed = mozcrash.log_crashes(logger, local_dump_dir, symbolsPath, test=self.lastTestSeen) else: crashed = mozcrash.check_for_crashes( local_dump_dir, symbolsPath, test_name=self.lastTestSeen) except: traceback.print_exc() finally: shutil.rmtree(local_dump_dir) self._devicemanager.removeDir(remote_dump_dir) return crashed
def check_for_crashes(self, symbols_path): """ Pull minidumps from the remote device and generate crash reports. Returns True if a crash was detected, or suspected. """ try: dump_dir = tempfile.mkdtemp() remote_dir = self.remote_minidumps if not self.device.is_dir(remote_dir): log.warning("No crash directory (%s) found on remote device" % remote_dir) return True self.device.pull(remote_dir, dump_dir) crashed = mozcrash.check_for_crashes(dump_dir, symbols_path, test_name="gtest") except Exception as e: log.error("unable to check for crashes: %s" % str(e)) crashed = True finally: try: shutil.rmtree(dump_dir) except Exception: log.warning("unable to remove directory: %s" % dump_dir) return crashed
def check_for_crashes(self, dump_directory=None, dump_save_path=None, test_name=None, quiet=False): """ Check for a possible crash and output stack trace. :param dump_directory: Directory to search for minidump files :param dump_save_path: Directory to save the minidump files to :param test_name: Name to use in the crash output :param quiet: If `True` don't print the PROCESS-CRASH message to stdout :returns: True if a crash was detected, otherwise False """ if not dump_directory: dump_directory = os.path.join(self.profile.profile, 'minidumps') crashed = False try: crashed = mozcrash.check_for_crashes(dump_directory, self.symbols_path, dump_save_path=dump_save_path, test_name=test_name, quiet=quiet) except: traceback.print_exc() return crashed
def run_one_test(self, prog, env, symbols_path=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) log.info("Running test %s", basename) with TemporaryDirectory() as tempdir: proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=CPPUnitTests.TEST_PROC_TIMEOUT) proc.wait() if proc.timedOut: log.testFail("%s | timed out after %d seconds", basename, CPPUnitTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): log.testFail("%s | test crashed", basename) return False result = proc.proc.returncode == 0 if not result: log.testFail("%s | test failed with return code %d", basename, proc.proc.returncode) return result
def test_symbol_path_url(self): """ Test that passing a URL as symbols_path correctly fetches the URL. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) def make_zipfile(): data = StringIO.StringIO() z = zipfile.ZipFile(data, 'w') z.writestr("symbols.txt", "abc/xyz") z.close() return data.getvalue() def get_symbols(req): headers = {} return (200, headers, make_zipfile()) httpd = mozhttpd.MozHttpd(port=0, urlhandlers=[{'method':'GET', 'path':'/symbols', 'function':get_symbols}]) httpd.start() symbol_url = urlparse.urlunsplit(('http', '%s:%d' % httpd.httpd.server_address, '/symbols','','')) self.assert_(mozcrash.check_for_crashes(self.tempdir, symbol_url, stackwalk_binary=self.stackwalk, quiet=True))
def test_symbol_path_not_present(self): open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, symbols_path=None, stackwalk_binary=self.stackwalk, quiet=True))
def run_one_test(self, prog, env, symbols_path=None, interactive=False): """ Run a single C++ unit test program remotely. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) remote_bin = posixpath.join(self.remote_bin_dir, basename) self.log.test_start(basename) buf = StringIO.StringIO() returncode = self.device.shell([remote_bin], buf, env=env, cwd=self.remote_home_dir, timeout=cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT) self.log.process_output(basename, "\n%s" % buf.getvalue(), command=[remote_bin]) with mozfile.TemporaryDirectory() as tempdir: self.device.getDirectory(self.remote_home_dir, tempdir) if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): self.log.test_end(basename, status='CRASH', expected='PASS') return False result = returncode == 0 if not result: self.log.test_end(basename, status='FAIL', expected='PASS', message=("test failed with return code %d" % returncode)) else: self.log.test_end(basename, status='PASS', expected='PASS') return result
def run_one_test(self, prog, env, symbols_path=None): """ Run a single C++ unit test program remotely. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) remote_bin = posixpath.join(self.remote_bin_dir, basename) log.info("Running test %s", basename) buf = StringIO.StringIO() returncode = self.device.shell( [remote_bin], buf, env=env, cwd=self.remote_home_dir, timeout=cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT) print >> sys.stdout, buf.getvalue() with mozfile.TemporaryDirectory() as tempdir: self.device.getDirectory(self.remote_home_dir, tempdir) if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): log.testFail("%s | test crashed", basename) return False result = returncode == 0 if not result: log.testFail("%s | test failed with return code %s", basename, returncode) return result
def run_gtest(self, prog, xre_path, symbols_path=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ self.xre_path = xre_path env = self.build_environment() log.info("Running gtest") proc = mozprocess.ProcessHandler([prog, "-unittest"], cwd=os.getcwd(), env=env) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=GTests.TEST_PROC_TIMEOUT, outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.timedOut: log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(os.getcwd(), symbols_path, test_name="gtest"): # mozcrash will output the log failure line for us. return False result = proc.proc.returncode == 0 if not result: log.testFail("gtest | test failed with return code %d", proc.proc.returncode) return result
def run_one_test(self, prog, env, symbols_path=None, interactive=False, timeout_factor=1): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * timeout_factor: An optional test-specific timeout multiplier. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) self.log.test_start(basename) with mozfile.TemporaryDirectory() as tempdir: if interactive: # For tests run locally, via mach, print output directly proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env, storeOutput=False) else: proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env, storeOutput=True, processOutputLine=lambda _: None) # TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. test_timeout = CPPUnitTests.TEST_PROC_TIMEOUT * timeout_factor proc.run(timeout=test_timeout, outputTimeout=CPPUnitTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.output: if self.fix_stack: procOutput = [self.fix_stack(l) for l in proc.output] else: procOutput = proc.output output = "\n%s" % "\n".join(procOutput) self.log.process_output(proc.pid, output, command=[prog]) if proc.timedOut: message = "timed out after %d seconds" % CPPUnitTests.TEST_PROC_TIMEOUT self.log.test_end(basename, status='TIMEOUT', expected='PASS', message=message) return False if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): self.log.test_end(basename, status='CRASH', expected='PASS') return False result = proc.proc.returncode == 0 if not result: self.log.test_end(basename, status='FAIL', expected='PASS', message=("test failed with return code %d" % proc.proc.returncode)) else: self.log.test_end(basename, status='PASS', expected='PASS') return result
def test_symbol_path_url(self): """ Test that passing a URL as symbols_path correctly fetches the URL. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) def make_zipfile(): data = StringIO.StringIO() z = zipfile.ZipFile(data, 'w') z.writestr("symbols.txt", "abc/xyz") z.close() return data.getvalue() def get_symbols(req): headers = {} return (200, headers, make_zipfile()) httpd = mozhttpd.MozHttpd(port=0, urlhandlers=[{ 'method': 'GET', 'path': '/symbols', 'function': get_symbols }]) httpd.start() symbol_url = urlparse.urlunsplit( ('http', '%s:%d' % httpd.httpd.server_address, '/symbols', '', '')) self.assert_( mozcrash.check_for_crashes(self.tempdir, symbol_url, stackwalk_binary=self.stackwalk, quiet=True))
def run_one_test(self, prog, env, symbols_path=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) log.info("Running test %s", basename) with TemporaryDirectory() as tempdir: proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=CPPUnitTests.TEST_PROC_TIMEOUT, outputTimeout=CPPUnitTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.timedOut: log.testFail("%s | timed out after %d seconds", basename, CPPUnitTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): log.testFail("%s | test crashed", basename) return False result = proc.proc.returncode == 0 if not result: log.testFail("%s | test failed with return code %d", basename, proc.proc.returncode) return result
def get_crashreports(directory, name=None): rc = 0 upload_path = os.environ.get("UPLOAD_PATH") if upload_path: # For automation, log the minidumps with stackwalk and get them moved to # the artifacts directory. fetches_dir = os.environ.get("MOZ_FETCHES_DIR") if not fetches_dir: raise Exception( "Unable to process minidump in automation because " "$MOZ_FETCHES_DIR is not set in the environment" ) stackwalk_binary = os.path.join( fetches_dir, "minidump_stackwalk", "minidump_stackwalk" ) if sys.platform == "win32": stackwalk_binary += ".exe" minidump_path = os.path.join(directory, "minidumps") rc = mozcrash.check_for_crashes( minidump_path, symbols_path=fetches_dir, stackwalk_binary=stackwalk_binary, dump_save_path=upload_path, test_name=name, ) return rc
def checkCrashesAtExit(): if mozcrash.check_for_crashes(dump_directory=os.path.join( PROFILE_DIR, 'minidumps'), symbols_path=SYMBOLS_PATH, test_name=TEST_NAME): print >> sys.stderr, 'TinderboxPrint: ' + TEST_NAME + '<br/><em class="testfail">CRASH</em>' sys.exit(1)
def run_one_test(self, prog, env, symbols_path=None): """ Run a single C++ unit test program remotely. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) remote_bin = posixpath.join(self.remote_bin_dir, basename) log.info("Running test %s", basename) buf = StringIO.StringIO() returncode = self.device.shell([remote_bin], buf, env=env, cwd=self.remote_home_dir, timeout=cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT) print >> sys.stdout, buf.getvalue() with mozfile.TemporaryDirectory() as tempdir: self.device.getDirectory(self.remote_home_dir, tempdir) if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): log.testFail("%s | test crashed", basename) return False result = returncode == 0 if not result: log.testFail("%s | test failed with return code %s", basename, returncode) return result
def check_for_crashes(self, dump_directory=None, dump_save_path=None, test_name=None, quiet=False): """ Check for a possible crash and output stack trace. :param dump_directory: Directory to search for minidump files :param dump_save_path: Directory to save the minidump files to :param test_name: Name to use in the crash output :param quiet: If `True` don't print the PROCESS-CRASH message to stdout :returns: True if a crash was detected, otherwise False """ if not dump_directory: dump_directory = os.path.join(self.profile.profile, "minidumps") if not dump_save_path: dump_save_path = self.dump_save_path try: logger = get_default_logger() if logger is not None: if test_name is None: test_name = "runner.py" self.crashed += mozcrash.log_crashes( logger, dump_directory, self.symbols_path, dump_save_path=dump_save_path, test=test_name ) else: crashed = mozcrash.check_for_crashes( dump_directory, self.symbols_path, dump_save_path=dump_save_path, test_name=test_name, quiet=quiet ) if crashed: self.crashed += 1 except: traceback.print_exc() return self.crashed
def run_gtest(self, prog, xre_path, cwd, symbols_path=None, utility_path=None, enable_webrender=False): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * cwd: The directory to run tests from (support files will be found in this direcotry). * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * utility_path: A path to a directory containing utility programs. currently used to locate a stack fixer to provide symbols symbols for assertion stacks. Return True if the program exits with a zero status, False otherwise. """ self.xre_path = xre_path env = self.build_environment(enable_webrender) log.info("Running gtest") if cwd and not os.path.isdir(cwd): os.makedirs(cwd) stream_output = mozprocess.StreamOutput(sys.stdout) process_output = stream_output if utility_path: stack_fixer = get_stack_fixer_function(utility_path, symbols_path) if stack_fixer: def f(line): return stream_output(stack_fixer(line)) process_output = f proc = mozprocess.ProcessHandler([prog, "-unittest", "--gtest_death_test_style=threadsafe"], cwd=cwd, env=env, processOutputLine=process_output) # TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=GTests.TEST_PROC_TIMEOUT, outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() log.info("gtest | process wait complete, returncode=%s" % proc.proc.returncode) if proc.timedOut: if proc.outputTimedOut: log.testFail("gtest | timed out after %d seconds without output", GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) else: log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(cwd, symbols_path, test_name="gtest"): # mozcrash will output the log failure line for us. return False result = proc.proc.returncode == 0 if not result: log.testFail("gtest | test failed with return code %d", proc.proc.returncode) return result
def test_nodumps(self): """ Test that check_for_crashes returns False if no dumps are present. """ self.stdouts.append(["this is some output"]) self.assertFalse(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk))
def check_for_crashes(self, browser_config, profile_dir, test_name): # check for minidumps minidumpdir = os.path.join(profile_dir, "minidumps") found = mozcrash.check_for_crashes(minidumpdir, browser_config["symbols_path"], test_name=test_name) mozfile.remove(minidumpdir) if found: raise TalosCrash("Found crashes after test run, terminating test")
def run_gtest(self, prog, xre_path, cwd, symbols_path=None, utility_path=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * cwd: The directory to run tests from (support files will be found in this direcotry). * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * utility_path: A path to a directory containing utility programs. currently used to locate a stack fixer to provide symbols symbols for assertion stacks. Return True if the program exits with a zero status, False otherwise. """ self.xre_path = xre_path env = self.build_environment() log.info("Running gtest") if cwd and not os.path.isdir(cwd): os.makedirs(cwd) stream_output = mozprocess.StreamOutput(sys.stdout) process_output = stream_output if utility_path: stack_fixer = get_stack_fixer_function(utility_path, symbols_path) if stack_fixer: process_output = lambda line: stream_output(stack_fixer(line)) proc = mozprocess.ProcessHandler([prog, "-unittest", "--gtest_death_test_style=threadsafe"], cwd=cwd, env=env, processOutputLine=process_output) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=GTests.TEST_PROC_TIMEOUT, outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.timedOut: if proc.outputTimedOut: log.testFail("gtest | timed out after %d seconds without output", GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) else: log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(cwd, symbols_path, test_name="gtest"): # mozcrash will output the log failure line for us. return False result = proc.proc.returncode == 0 if not result: log.testFail("gtest | test failed with return code %d", proc.proc.returncode) return result
def test_stackwalk_envvar(self): """ Test that check_for_crashes uses the MINIDUMP_STACKWALK environment var. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) os.environ["MINIDUMP_STACKWALK"] = self.stackwalk self.assert_(mozcrash.check_for_crashes(self.tempdir, "symbols_path", quiet=True)) del os.environ["MINIDUMP_STACKWALK"]
def test_simple(self): """ Test that check_for_crashes returns True if a dump is present. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk))
def check_for_crashes(self, dump_directory, test_name=None): crashed = False try: crashed = mozcrash.check_for_crashes(dump_directory, self.symbols_path, test_name=test_name) except: traceback.print_exc() return crashed
def check_for_crashes(self, browser_config, minidump_dir, test_name): # check for minidumps found = mozcrash.check_for_crashes(minidump_dir, browser_config['symbols_path'], test_name=test_name) mozfile.remove(minidump_dir) if found: raise TalosCrash("Found crashes after test run, terminating test")
def test_nodumps(self): """ Test that check_for_crashes returns False if no dumps are present. """ self.stdouts.append(["this is some output"]) self.assertFalse(mozcrash.check_for_crashes(self.tempdir, symbols_path='symbols_path', stackwalk_binary=self.stackwalk, quiet=True))
def wrapper(dump_directory=fspath(tmpdir), symbols_path='symbols_path', stackwalk_binary=fspath(stackwalk), dump_save_path=None, test_name=None, quiet=True): return mozcrash.check_for_crashes(dump_directory, symbols_path, stackwalk_binary, dump_save_path, test_name, quiet)
def run_one_test(self, prog, env, symbols_path=None, interactive=False, timeout_factor=1): """ Run a single C++ unit test program remotely. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * timeout_factor: An optional test-specific timeout multiplier. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) remote_bin = posixpath.join(self.remote_bin_dir, basename) self.log.test_start(basename) test_timeout = cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT * timeout_factor try: output = self.device.shell_output(remote_bin, env=env, cwd=self.remote_home_dir, timeout=test_timeout) returncode = 0 except ADBTimeoutError: raise except ADBProcessError as e: output = e.adb_process.stdout returncode = e.adb_process.exitcode self.log.process_output(basename, "\n%s" % output, command=[remote_bin]) with mozfile.TemporaryDirectory() as tempdir: self.device.pull(self.remote_home_dir, tempdir) if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): self.log.test_end(basename, status="CRASH", expected="PASS") return False result = returncode == 0 if not result: self.log.test_end( basename, status="FAIL", expected="PASS", message=("test failed with return code %s" % returncode), ) else: self.log.test_end(basename, status="PASS", expected="PASS") return result
def test_simple(self): """ Test that check_for_crashes returns True if a dump is present. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, symbols_path='symbols_path', stackwalk_binary=self.stackwalk, quiet=True))
def run_one_test(self, prog, env, symbols_path=None, interactive=False, timeout_factor=1): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * timeout_factor: An optional test-specific timeout multiplier. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) self.log.test_start(basename) with mozfile.TemporaryDirectory() as tempdir: if interactive: # For tests run locally, via mach, print output directly proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env, storeOutput=False) else: proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env, storeOutput=True, processOutputLine=lambda _: None) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. test_timeout = CPPUnitTests.TEST_PROC_TIMEOUT * timeout_factor proc.run(timeout=test_timeout, outputTimeout=CPPUnitTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.output: output = "\n%s" % "\n".join(proc.output) self.log.process_output(proc.pid, output, command=[prog]) if proc.timedOut: message = "timed out after %d seconds" % CPPUnitTests.TEST_PROC_TIMEOUT self.log.test_end(basename, status='TIMEOUT', expected='PASS', message=message) return False if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): self.log.test_end(basename, status='CRASH', expected='PASS') return False result = proc.proc.returncode == 0 if not result: self.log.test_end(basename, status='FAIL', expected='PASS', message=("test failed with return code %d" % proc.proc.returncode)) else: self.log.test_end(basename, status='PASS', expected='PASS') return result
def test_stackwalk_envvar(self): """ Test that check_for_crashes uses the MINIDUMP_STACKWALK environment var. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") self.stdouts.append(["this is some output"]) os.environ['MINIDUMP_STACKWALK'] = self.stackwalk self.assert_(mozcrash.check_for_crashes(self.tempdir, symbols_path='symbols_path', quiet=True)) del os.environ['MINIDUMP_STACKWALK']
def check_for_crashes(self, dump_directory=None, test_name=None): if not dump_directory: dump_directory = os.path.join(self.profile.profile, 'minidumps') crashed = False try: crashed = mozcrash.check_for_crashes(dump_directory, self.symbols_path, test_name=test_name) except: traceback.print_exc() return crashed
def test_save_path_not_present(self): """ Test that dump_save_path works when the directory doesn't exist. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") save_path = os.path.join(self.tempdir, "saved") self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk, dump_save_path=save_path)) self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp")))
def checkForCrashes(self, directory, symbolsPath): crashed = False remote_dump_dir = self._remoteProfile + "/minidumps" if self._devicemanager.dirExists(remote_dump_dir): local_dump_dir = tempfile.mkdtemp() self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) try: crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen) finally: shutil.rmtree(local_dump_dir) self._devicemanager.removeDir(remote_dump_dir) return crashed
def cleanupAndCheckForCrashes(self, browser_config, profile_dir, test_name): """cleanup browser processes and process crashes if found""" # cleanup processes cleanup_result = self._ffprocess.cleanupProcesses( browser_config['process'], browser_config['child_process'], browser_config['browser_wait']) # find stackwalk binary if platform.system() in ('Windows', 'Microsoft'): stackwalkpaths = ['win32', 'minidump_stackwalk.exe'] elif platform.system() == 'Linux': if '64' in platform.architecture()[0]: #are we 64 bit? stackwalkpaths = ['linux64', 'minidump_stackwalk'] else: stackwalkpaths = ['linux', 'minidump_stackwalk'] elif platform.system() == 'Darwin': stackwalkpaths = ['osx', 'minidump_stackwalk'] else: # no minidump_stackwalk available for your platform return stackwalkbin = os.path.join(os.path.dirname(__file__), 'breakpad', *stackwalkpaths) assert os.path.exists( stackwalkbin ), "minidump_stackwalk binary not found: %s" % stackwalkbin # look for minidumps minidumpdir = os.path.join(profile_dir, 'minidumps') if browser_config['remote'] == True: minidumpdir = tempfile.mkdtemp() try: remoteminidumpdir = profile_dir + '/minidumps/' if self._ffprocess.testAgent.dirExists(remoteminidumpdir): self._ffprocess.testAgent.getDirectory( remoteminidumpdir, minidumpdir) except mozdevice.DMError: print "Remote Device Error: Error getting crash minidumps from device" raise found = mozcrash.check_for_crashes(minidumpdir, browser_config['symbols_path'], stackwalk_binary=stackwalkbin, test_name=test_name) if browser_config['remote'] == True: # cleanup dumps on remote self._ffprocess.testAgent.removeDir(remoteminidumpdir) self._hostproc.removeDirectory(minidumpdir) if found: raise talosCrash("Found crashes after test run, terminating test")
def checkForCrashes(self, directory, symbolsPath): crashed = False remote_dump_dir = self._remoteProfile + '/minidumps' if self._devicemanager.dirExists(remote_dump_dir): local_dump_dir = tempfile.mkdtemp() self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) try: crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen) finally: shutil.rmtree(local_dump_dir) self._devicemanager.removeDir(remote_dump_dir) return crashed
def check_for_crashes(self, dump_directory=None, dump_save_path=None, test_name=None, quiet=False): """Check for possible crashes and output the stack traces. :param dump_directory: Directory to search for minidump files :param dump_save_path: Directory to save the minidump files to :param test_name: Name to use in the crash output :param quiet: If `True` don't print the PROCESS-CRASH message to stdout :returns: Number of crashes which have been detected since the last invocation """ crash_count = 0 if not dump_directory: dump_directory = os.path.join(self.profile.profile, "minidumps") if not dump_save_path: dump_save_path = self.dump_save_path if not test_name: test_name = "runner.py" try: if self.logger: if mozcrash: crash_count = mozcrash.log_crashes( self.logger, dump_directory, self.symbols_path, dump_save_path=dump_save_path, test=test_name, ) else: self.logger.warning("Can not log crashes without mozcrash") else: if mozcrash: crash_count = mozcrash.check_for_crashes( dump_directory, self.symbols_path, dump_save_path=dump_save_path, test_name=test_name, quiet=quiet, ) self.crashed += crash_count except Exception: traceback.print_exc() return crash_count
def test_save_path_envvar(self): """ Test that the MINDUMP_SAVE_PATH environment variable works. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") save_path = os.path.join(self.tempdir, "saved") os.mkdir(save_path) self.stdouts.append(["this is some output"]) os.environ['MINIDUMP_SAVE_PATH'] = save_path self.assert_(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk)) del os.environ['MINIDUMP_SAVE_PATH'] self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp")))
def test_save_path(self): """ Test that dump_save_path works. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") save_path = os.path.join(self.tempdir, "saved") os.mkdir(save_path) self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk, dump_save_path=save_path, quiet=True)) self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp")))
def test_save_path_not_present(self): """ Test that dump_save_path works when the directory doesn't exist. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") save_path = os.path.join(self.tempdir, "saved") self.stdouts.append(["this is some output"]) self.assert_( mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk, dump_save_path=save_path, quiet=True)) self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp")))
def check_for_crashes(self, dump_directory=None, dump_save_path=None, test_name=None, quiet=False): """ Check for a possible crash and output stack trace. :param dump_directory: Directory to search for minidump files :param dump_save_path: Directory to save the minidump files to :param test_name: Name to use in the crash output :param quiet: If `True` don't print the PROCESS-CRASH message to stdout :returns: True if a crash was detected, otherwise False """ if not dump_directory: dump_directory = os.path.join(self.profile.profile, 'minidumps') if not dump_save_path: dump_save_path = self.dump_save_path try: logger = get_default_logger() if logger is not None: if test_name is None: test_name = "runner.py" if mozcrash: self.crashed += mozcrash.log_crashes( logger, dump_directory, self.symbols_path, dump_save_path=dump_save_path, test=test_name) else: logger.warning("Can not log crashes without mozcrash") else: if mozcrash: crashed = mozcrash.check_for_crashes( dump_directory, self.symbols_path, dump_save_path=dump_save_path, test_name=test_name, quiet=quiet) if crashed: self.crashed += 1 else: logger.warning("Can not log crashes without mozcrash") except: traceback.print_exc() return self.crashed
def run_one_test(self, prog, env, symbols_path=None, interactive=False, timeout_factor=1): """ Run a single C++ unit test program remotely. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. * timeout_factor: An optional test-specific timeout multiplier. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) remote_bin = posixpath.join(self.remote_bin_dir, basename) self.log.test_start(basename) buf = StringIO.StringIO() test_timeout = cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT * \ timeout_factor returncode = self.device.shell([remote_bin], buf, env=env, cwd=self.remote_home_dir, timeout=test_timeout) self.log.process_output(basename, "\n%s" % buf.getvalue(), command=[remote_bin]) with mozfile.TemporaryDirectory() as tempdir: self.device.getDirectory(self.remote_home_dir, tempdir) if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): self.log.test_end(basename, status='CRASH', expected='PASS') return False result = returncode == 0 if not result: self.log.test_end(basename, status='FAIL', expected='PASS', message=("test failed with return code %s" % returncode)) else: self.log.test_end(basename, status='PASS', expected='PASS') return result
def check_for_crashes(self, symbols_path): remote_dump_dirs = [posixpath.join(p, 'minidumps') for p in self.remote_profiles] crashed = False for remote_dump_dir in remote_dump_dirs: local_dump_dir = tempfile.mkdtemp() self.dm.getDirectory(remote_dump_dir, local_dump_dir) try: if mozcrash.check_for_crashes(local_dump_dir, symbols_path): crashed = True except: traceback.print_exc() finally: shutil.rmtree(local_dump_dir) self.dm.removeDir(remote_dump_dir) return crashed
def check_for_crashes(self, symbols_path, last_test=None): crashed = False remote_dump_dir = posixpath.join(self.remote_profile, 'minidumps') self.log.info("checking for crashes in '%s'" % remote_dump_dir) if self.dm.dirExists(remote_dump_dir): local_dump_dir = tempfile.mkdtemp() self.dm.getDirectory(remote_dump_dir, local_dump_dir) try: crashed = mozcrash.check_for_crashes(local_dump_dir, symbols_path, test_name=last_test) except: traceback.print_exc() finally: shutil.rmtree(local_dump_dir) self.dm.removeDir(remote_dump_dir) return crashed
def checkForCrashes(self, directory, symbolsPath): crashed = False remote_dump_dir = self._remoteProfile + '/minidumps' print "checking for crashes in '%s'" % remote_dump_dir if self._devicemanager.dirExists(remote_dump_dir): local_dump_dir = tempfile.mkdtemp() self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) try: crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen) except: traceback.print_exc() finally: shutil.rmtree(local_dump_dir) self._devicemanager.removeDir(remote_dump_dir) return crashed
def check_for_crashes(self): remote_dump_dirs = [posixpath.join(p, 'minidumps') for p in self.remote_profiles] crashed = False for remote_dump_dir in remote_dump_dirs: local_dump_dir = tempfile.mkdtemp() self.dm.getDirectory(remote_dump_dir, local_dump_dir) try: if mozcrash.check_for_crashes(local_dump_dir, self.symbols_path): crashed = True except: traceback.print_exc() finally: shutil.rmtree(local_dump_dir) self.dm.removeDir(remote_dump_dir) return crashed
def test_save_path(self): """ Test that dump_save_path works. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") save_path = os.path.join(self.tempdir, "saved") os.mkdir(save_path) self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, symbols_path='symbols_path', stackwalk_binary=self.stackwalk, dump_save_path=save_path, quiet=True)) self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) self.assert_(os.path.isfile(os.path.join(save_path, "test.extra")))
def test_save_path_envvar(self): """ Test that the MINDUMP_SAVE_PATH environment variable works. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") save_path = os.path.join(self.tempdir, "saved") os.mkdir(save_path) self.stdouts.append(["this is some output"]) os.environ['MINIDUMP_SAVE_PATH'] = save_path self.assert_( mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk, quiet=True)) del os.environ['MINIDUMP_SAVE_PATH'] self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp")))
def check_for_crashes(self, dump_directory=None, dump_save_path=None, test_name=None, quiet=False): """Check for possible crashes and output the stack traces. :param dump_directory: Directory to search for minidump files :param dump_save_path: Directory to save the minidump files to :param test_name: Name to use in the crash output :param quiet: If `True` don't print the PROCESS-CRASH message to stdout :returns: Number of crashes which have been detected since the last invocation """ crash_count = 0 if not dump_directory: dump_directory = os.path.join(self.profile.profile, 'minidumps') if not dump_save_path: dump_save_path = self.dump_save_path if not test_name: test_name = "runner.py" try: if self.logger: if mozcrash: crash_count = mozcrash.log_crashes( self.logger, dump_directory, self.symbols_path, dump_save_path=dump_save_path, test=test_name) else: self.logger.warning("Can not log crashes without mozcrash") else: if mozcrash: crash_count = mozcrash.check_for_crashes( dump_directory, self.symbols_path, dump_save_path=dump_save_path, test_name=test_name, quiet=quiet) self.crashed += crash_count except: traceback.print_exc() return crash_count
def check_for_crashes(self, dump_directory=None, test_name=None): """Check for a possible crash and output stack trace :param dump_directory: Directory to search for minidump files :param test_name: Name to use in the crash output """ if not dump_directory: dump_directory = os.path.join(self.profile.profile, "minidumps") crashed = False try: crashed = mozcrash.check_for_crashes(dump_directory, self.symbols_path, test_name=test_name) except: traceback.print_exc() return crashed
def test_save_path_isfile(self): """ Test that dump_save_path works when the directory doesn't exist, but a file with the same name exists. """ open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") save_path = os.path.join(self.tempdir, "saved") open(save_path, "w").write("junk") self.stdouts.append(["this is some output"]) self.assert_(mozcrash.check_for_crashes(self.tempdir, 'symbols_path', stackwalk_binary=self.stackwalk, dump_save_path=save_path, quiet=True)) self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) self.assert_(os.path.isfile(os.path.join(save_path, "test.extra")))
def run_one_test(prog, env, symbols_path=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ basename = os.path.basename(prog) log.info("Running test %s", basename) with TemporaryDirectory() as tempdir: proc = mozprocess.ProcessHandler([prog], cwd=tempdir, env=env) proc.run() timeout = 300 proc.processOutput(timeout=timeout) if proc.timedOut: log.testFail("%s | timed out after %d seconds", basename, timeout) return False proc.waitForFinish(timeout=timeout) if proc.timedOut: log.testFail("%s | timed out after %d seconds", basename, timeout) return False if mozcrash.check_for_crashes(tempdir, symbols_path, test_name=basename): log.testFail("%s | test crashed", basename) return False result = proc.proc.returncode == 0 if not result: log.testFail("%s | test failed with return code %d", basename, proc.proc.returncode) return result
def run_gtest(self, prog, xre_path, symbols_path=None, cwd=None): """ Run a single C++ unit test program. Arguments: * prog: The path to the test program to run. * env: The environment to use for running the program. * symbols_path: A path to a directory containing Breakpad-formatted symbol files for producing stack traces on crash. Return True if the program exits with a zero status, False otherwise. """ self.xre_path = xre_path env = self.build_environment() log.info("Running gtest") if cwd and not os.path.isdir(cwd): os.makedirs(cwd) proc = mozprocess.ProcessHandler([prog, "-unittest"], cwd=cwd, env=env) #TODO: After bug 811320 is fixed, don't let .run() kill the process, # instead use a timeout in .wait() and then kill to get a stack. proc.run(timeout=GTests.TEST_PROC_TIMEOUT, outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) proc.wait() if proc.timedOut: log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) return False if mozcrash.check_for_crashes(os.getcwd(), symbols_path, test_name="gtest"): # mozcrash will output the log failure line for us. return False result = proc.proc.returncode == 0 if not result: log.testFail("gtest | test failed with return code %d", proc.proc.returncode) return result
def runApp(self, profile, binary, cmdargs, env, timeout=None, debuggerInfo=None, symbolsPath=None, options=None): def timeoutHandler(): self.handleTimeout(timeout, proc, options.utilityPath, debuggerInfo) interactive = False debug_args = None if debuggerInfo: interactive = debuggerInfo.interactive debug_args = [debuggerInfo.path] + debuggerInfo.args outputHandler = self.OutputHandler(harness=self, utilityPath=options.utilityPath, symbolsPath=symbolsPath, dump_screen_on_timeout=not debuggerInfo, ) kp_kwargs = { 'kill_on_timeout': False, 'cwd': SCRIPT_DIRECTORY, 'onTimeout': [timeoutHandler], 'processOutputLine': [outputHandler], } if interactive: # If an interactive debugger is attached, # don't use timeouts, and don't capture ctrl-c. timeout = None signal.signal(signal.SIGINT, lambda sigid, frame: None) if mozinfo.info.get('appname') == 'b2g' and mozinfo.info.get('toolkit') != 'gonk': runner_cls = mozrunner.Runner else: runner_cls = mozrunner.runners.get(mozinfo.info.get('appname', 'firefox'), mozrunner.Runner) runner = runner_cls(profile=profile, binary=binary, process_class=mozprocess.ProcessHandlerMixin, cmdargs=cmdargs, env=env, process_args=kp_kwargs) runner.start(debug_args=debug_args, interactive=interactive, outputTimeout=timeout) proc = runner.process_handler status = runner.wait() runner.process_handler = None if timeout is None: didTimeout = False else: didTimeout = proc.didTimeout if status: log.info("TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s", self.lastTestSeen, status) else: self.lastTestSeen = 'Main app process exited normally' crashed = mozcrash.check_for_crashes(os.path.join(profile.profile, "minidumps"), symbolsPath, test_name=self.lastTestSeen) runner.cleanup() if not status and crashed: status = 1 return status