Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Unified Diff: tools/utils.py

Issue 2692883002: Enable support for coredump archiving on windows (Closed)
Patch Set: Addressed comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/testing/dart/test_progress.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/utils.py
diff --git a/tools/utils.py b/tools/utils.py
index 87ee9075e51f6f8cb749008076bab3a14717bcd5..da2cd28892b25e9d8eb5eb306e86d623bd5a6a76 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -6,6 +6,7 @@
# scripts.
import commands
+import contextlib
import datetime
import glob
import imp
@@ -606,7 +607,7 @@ def CheckedInSdkPath():
osname = osdict[system]
except KeyError:
print >>sys.stderr, ('WARNING: platform "%s" not supported') % (system)
- return None;
+ return None
tools_dir = os.path.dirname(os.path.realpath(__file__))
return os.path.join(tools_dir,
'sdks',
@@ -649,6 +650,23 @@ def CheckedInSdkCheckExecutable():
return False
+def CheckLinuxCoreDumpPattern(fatal=False):
+ core_pattern_file = '/proc/sys/kernel/core_pattern'
+ core_pattern = open(core_pattern_file).read()
+
+ expected_core_pattern = 'core.%p'
+ if core_pattern.strip() != expected_core_pattern:
+ if fatal:
+ message = ('Invalid core_pattern configuration. '
+ 'The configuration of core dump handling is *not* correct for '
+ 'a buildbot. The content of {0} must be "{1}" instead of "{2}".'
+ .format(core_pattern_file, expected_core_pattern, core_pattern))
+ raise Exception(message)
+ else:
+ return False
+ return True
+
+
class TempDir(object):
def __init__(self, prefix=''):
self._temp_dir = None
@@ -674,100 +692,164 @@ class ChangedWorkingDirectory(object):
print "Enter directory = ", self._old_cwd
os.chdir(self._old_cwd)
-class CoreDump(object):
- def __init__(self, test, core, binary):
+
+class UnexpectedCrash(object):
+ def __init__(self, test, pid, binary):
self.test = test
- self.core = core
+ self.pid = pid
self.binary = binary
def __str__(self):
- return "%s: %s %s" % (self.test, self.binary, self.core)
+ return "%s: %s %s" % (self.test, self.binary, self.pid)
-class CoreDumpArchiver(object):
- """This class reads coredumps file written by UnexpectedCrashDumpArchiver
- into the current working directory and uploads all cores and binaries
- listed in it into Cloud Storage (see tools/testing/dart/test_progress.dart).
- """
- def __init__(self, args):
- self._enabled = '--copy-coredumps' in args and GuessOS() == 'linux'
- self._search_dir = os.getcwd()
- self._bucket = 'dart-temp-crash-archive'
+class PosixCoredumpEnabler(object):
+ def __init__(self):
self._old_limits = None
def __enter__(self):
- if not self._enabled:
- return
-
- # Cleanup any stale coredumps
- if self._cleanup():
- print "WARNING: Found and removed stale coredumps"
-
self._old_limits = resource.getrlimit(resource.RLIMIT_CORE)
# Bump core limits to unlimited if core_pattern is correctly configured.
- if self._check_core_dump_pattern(fatal=False):
+ if CheckLinuxCoreDumpPattern(fatal=False):
resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
def __exit__(self, *_):
- if not self._enabled:
- return
+ resource.setrlimit(resource.RLIMIT_CORE, self._old_limits)
+ CheckLinuxCoreDumpPattern(fatal=True)
- try:
- # Restore old core limit.
- resource.setrlimit(resource.RLIMIT_CORE, self._old_limits)
+class WindowsCoredumpEnabler(object):
+ """Configure Windows Error Reporting to store crash dumps.
- # Check that kernel was correctly configured to use core.%p
- # core_pattern.
- self._check_core_dump_pattern(fatal=True)
+ The documentation can be found here:
+ https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181.aspx
+ """
- coredumps = self._find_coredumps()
- if coredumps:
- # If we get a ton of crashes, only archive 10 dumps.
- archive_coredumps = coredumps[:10]
- print 'Archiving coredumps:'
- for core in archive_coredumps:
- print '----> %s' % core
+ WINDOWS_COREDUMP_FOLDER = r'crashes'
- sys.stdout.flush()
- self._archive(archive_coredumps)
+ WER_NAME = r'SOFTWARE\Microsoft\Windows\Windows Error Reporting'
+ WER_LOCALDUMPS_NAME = r'%s\LocalDumps' % WER_NAME
+ IMGEXEC_NAME = (r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
+ r'\Image File Execution Options\WerFault.exe')
- finally:
- self._cleanup()
+ def __init__(self):
+ # Depending on whether we're in cygwin or not we use a different import.
+ try:
+ import winreg
+ except ImportError:
+ import _winreg as winreg
+ self.winreg = winreg
- def _cleanup(self):
- found = False
- for core in glob.glob(os.path.join(self._search_dir, 'core.*')):
- found = True
- os.unlink(core)
- for binary in glob.glob(os.path.join(self._search_dir, 'binary.*')):
- found = True
- os.unlink(binary)
+ def __enter__(self):
+ # We want 32 and 64 bit coredumps to land in the same coredump directory.
+ for sam in [self.winreg.KEY_WOW64_64KEY, self.winreg.KEY_WOW64_32KEY]:
+ # In case WerFault.exe was prevented from executing, we fix it here.
+ # TODO(kustermann): Remove this once https://crbug.com/691971 is fixed.
+ self._prune_existing_key(
+ self.winreg.HKEY_LOCAL_MACHINE, self.IMGEXEC_NAME, sam)
+
+ # Create (or open) the WER keys.
+ with self.winreg.CreateKeyEx(
+ self.winreg.HKEY_LOCAL_MACHINE, self.WER_NAME, 0,
+ self.winreg.KEY_ALL_ACCESS | sam) as wer:
+ with self.winreg.CreateKeyEx(
+ self.winreg.HKEY_LOCAL_MACHINE, self.WER_LOCALDUMPS_NAME, 0,
+ self.winreg.KEY_ALL_ACCESS | sam) as wer_localdumps:
+ # Prevent any modal UI dialog & disable normal windows error reporting
+ # TODO(kustermann): Remove this once https://crbug.com/691971 is fixed
+ self.winreg.SetValueEx(wer, "DontShowUI", 0, self.winreg.REG_DWORD, 1)
+ self.winreg.SetValueEx(wer, "Disabled", 0, self.winreg.REG_DWORD, 1)
+
+ coredump_folder = os.path.join(
+ os.getcwd(), WindowsCoredumpEnabler.WINDOWS_COREDUMP_FOLDER)
+
+ # Create the directory which will contain the dumps
+ if not os.path.exists(coredump_folder):
+ os.mkdir(coredump_folder)
+
+ # Do full dumps (not just mini dumps), keep max 100 dumps and specify
+ # folder.
+ self.winreg.SetValueEx(
+ wer_localdumps, "DumpType", 0, self.winreg.REG_DWORD, 2)
+ self.winreg.SetValueEx(
+ wer_localdumps, "DumpCount", 0, self.winreg.REG_DWORD, 200)
+ self.winreg.SetValueEx(
+ wer_localdumps, "DumpFolder", 0, self.winreg.REG_EXPAND_SZ,
+ coredump_folder)
+
+ def __exit__(self, *_):
+ # We remove the local dumps settings after running the tests.
+ for sam in [self.winreg.KEY_WOW64_64KEY, self.winreg.KEY_WOW64_32KEY]:
+ with self.winreg.CreateKeyEx(
+ self.winreg.HKEY_LOCAL_MACHINE, self.WER_LOCALDUMPS_NAME, 0,
+ self.winreg.KEY_ALL_ACCESS | sam) as wer_localdumps:
+ self.winreg.DeleteValue(wer_localdumps, 'DumpType')
+ self.winreg.DeleteValue(wer_localdumps, 'DumpCount')
+ self.winreg.DeleteValue(wer_localdumps, 'DumpFolder')
+
+ def _prune_existing_key(self, key, subkey, wowbit):
+ handle = None
+
+ # If the open fails, the key doesn't exist and it's fine.
try:
- os.unlink(os.path.join(self._search_dir, 'coredumps'))
- found = True
- except:
+ handle = self.winreg.OpenKey(
+ key, subkey, 0, self.winreg.KEY_READ | wowbit)
+ except OSError:
pass
- return found
+ # If the key exists then we delete it. If the deletion does not work, we
+ # let the exception through.
+ if handle:
+ handle.Close()
+ self.winreg.DeleteKeyEx(key, subkey, wowbit, 0)
- def _find_coredumps(self):
- """Load coredumps file. Each line has the following format:
+class BaseCoreDumpArchiver(object):
+ """This class reads coredumps file written by UnexpectedCrashDumpArchiver
+ into the current working directory and uploads all cores and binaries
+ listed in it into Cloud Storage (see tools/testing/dart/test_progress.dart).
+ """
- test-name,core-file,binary-file
- """
+ # test.dart will write a line for each unexpected crash into this file.
+ _UNEXPECTED_CRASHES_FILE = "unexpected-crashes"
+
+ def __init__(self):
+ self._bucket = 'dart-temp-crash-archive'
+ self._binaries_dir = os.getcwd()
+
+ def __enter__(self):
+ # Cleanup any stale files
+ if self._cleanup():
+ print "WARNING: Found and removed stale coredumps"
+
+ def __exit__(self, *_):
try:
- with open('coredumps') as f:
- return [CoreDump(*ln.strip('\n').split(',')) for ln in f.readlines()]
- except:
- return []
+ crashes = self._find_unexpected_crashes()
+ if crashes:
+ # If we get a ton of crashes, only archive 10 dumps.
+ archive_crashes = crashes[:10]
+ print 'Archiving coredumps for crash (if possible):'
+ for crash in archive_crashes:
+ print '----> %s' % crash
+
+ sys.stdout.flush()
+ self._archive(archive_crashes)
+
+ finally:
+ self._cleanup()
- def _archive(self, coredumps):
+ def _archive(self, crashes):
files = set()
- for core in coredumps:
- files.add(core.core)
- files.add(core.binary)
+ missing = []
+ for crash in crashes:
+ files.add(crash.binary)
+ core = self._find_coredump_file(crash)
+ if core:
+ files.add(core)
+ else:
+ missing.append(crash)
self._upload(files)
+ if missing:
+ raise Exception('Missing crash dumps for: %s' % ', '.join(missing))
def _upload(self, files):
bot_utils = GetBotUtils()
@@ -805,21 +887,84 @@ class CoreDumpArchiver(object):
os.unlink(tarname)
print '--- Done ---\n'
- def _check_core_dump_pattern(self, fatal=False):
- core_pattern_file = '/proc/sys/kernel/core_pattern'
- core_pattern = open(core_pattern_file).read()
-
- expected_core_pattern = 'core.%p'
- if core_pattern.strip() != expected_core_pattern:
- if fatal:
- message = ('Invalid core_pattern configuration. '
- 'The configuration of core dump handling is *not* correct for '
- 'a buildbot. The content of {0} must be "{1}" instead of "{2}".'
- .format(core_pattern_file, expected_core_pattern, core_pattern))
- raise Exception(message)
- else:
- return False
- return True
+ def _find_unexpected_crashes(self):
+ """Load coredumps file. Each line has the following format:
+
+ test-name,pid,binary-file
+ """
+ try:
+ with open(BaseCoreDumpArchiver._UNEXPECTED_CRASHES_FILE) as f:
+ return [UnexpectedCrash(*ln.strip('\n').split(',')) for ln in f.readlines()]
+ except:
+ return []
+
+ def _cleanup(self):
+ found = False
+ if os.path.exists(BaseCoreDumpArchiver._UNEXPECTED_CRASHES_FILE):
+ os.unlink(BaseCoreDumpArchiver._UNEXPECTED_CRASHES_FILE)
+ found = True
+ for binary in glob.glob(os.path.join(self._binaries_dir, 'binary.*')):
+ found = True
+ os.unlink(binary)
+ return found
+
+class LinuxCoreDumpArchiver(BaseCoreDumpArchiver):
+ def __init__(self):
+ super(self.__class__, self).__init__()
+ self._search_dir = os.getcwd()
+
+ def _cleanup(self):
+ found = super(self.__class__, self)._cleanup()
+ for core in glob.glob(os.path.join(self._search_dir, 'core.*')):
+ found = True
+ os.unlink(core)
+ return found
+
+ def _find_coredump_file(self, crash):
+ core_filename = os.path.join(self._search_dir, 'core.%s' % crash.pid)
+ if os.path.exists(core_filename):
+ return core_filename
+
+class WindowsCoreDumpArchiver(BaseCoreDumpArchiver):
+ def __init__(self):
+ super(self.__class__, self).__init__()
+ self._search_dir = os.path.join(
+ os.getcwd(), WindowsCoredumpEnabler.WINDOWS_COREDUMP_FOLDER)
+
+ def _cleanup(self):
+ found = super(self.__class__, self)._cleanup()
+ for core in glob.glob(os.path.join(self._search_dir, '*')):
+ found = True
+ os.unlink(core)
+ return found
+
+ def _find_coredump_file(self, crash):
+ pattern = os.path.join(self._search_dir, '*.%s.*' % crash.pid)
+ for core_filename in glob.glob(pattern):
+ return core_filename
+
+@contextlib.contextmanager
+def NooptCoreDumpArchiver():
+ yield
+
+
+def CoreDumpArchiver(args):
+ enabled = '--copy-coredumps' in args
+
+ if not enabled:
+ return NooptCoreDumpArchiver()
+
+ osname = GuessOS()
+ if osname == 'linux':
+ return contextlib.nested(PosixCoredumpEnabler(),
+ LinuxCoreDumpArchiver())
+ elif osname == 'win32':
+ return contextlib.nested(WindowsCoredumpEnabler(),
+ WindowsCoreDumpArchiver())
+ else:
+ # We don't have support for MacOS yet.
+ assert osname == 'macos'
+ return NooptCoreDumpArchiver()
if __name__ == "__main__":
import sys
« no previous file with comments | « tools/testing/dart/test_progress.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698