Chromium Code Reviews| Index: tools/utils.py |
| diff --git a/tools/utils.py b/tools/utils.py |
| index 161ea4bb1879ccb04b43e641e1130cff82b1931e..aa220972da4bf426569f88433d2bef0a20e38330 100644 |
| --- a/tools/utils.py |
| +++ b/tools/utils.py |
| @@ -15,6 +15,15 @@ import shutil |
| import subprocess |
| import tempfile |
| import sys |
| +import uuid |
| + |
| +try: |
| + import resource |
| + import tarfile |
| + from bots import bot_utils |
| + from glob import glob |
|
kustermann
2017/01/18 17:12:36
Does this need to be in the try?
Vyacheslav Egorov (Google)
2017/01/18 19:57:00
Done.
|
| +except: |
| + pass |
| class Version(object): |
| def __init__(self, channel, major, minor, patch, prerelease, |
| @@ -657,6 +666,122 @@ class ChangedWorkingDirectory(object): |
| print "Enter directory = ", self._old_cwd |
| os.chdir(self._old_cwd) |
| +# This class finds and archives all core.* files from the current working |
| +# directory and all binaries copied by UnexpectedCrashDumpArchiver into |
|
kustermann
2017/01/18 17:12:35
add `(from tools/testing/dart/test_progress.dart)`
Vyacheslav Egorov (Google)
2017/01/18 19:57:00
Done.
|
| +# the current working directory. |
| +class CoreDumpArchiver(object): |
| + def __init__(self, args): |
| + self._enabled = '--copy-coredumps' in args and GuessOS() == 'linux' |
| + self._search_dir = os.getcwd() |
| + self._bucket = 'dart-temp-crash-archive' |
| + self._old_limits = None |
| + |
| + def __enter__(self): |
| + if not self._enabled: |
| + return |
| + |
| + # Cleanup any stale coredumps |
| + coredumps = self._find_coredumps() |
| + if coredumps: |
| + print "WARNING: Found stale coredumps, removing" |
| + MarkCurrentStepWarning() |
| + self._remove_coredumps(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): |
| + resource.setrlimit(resource.RLIMIT_CORE, (-1, -1)) |
| + |
| + def __exit__(self, *_): |
| + if not self._enabled: |
| + return |
| + |
| + # Restore old core limit. |
| + resource.setrlimit(resource.RLIMIT_CORE, self._old_limits) |
| + |
| + # Check that kernel was correctly configured to use core.%p |
| + # core_pattern. |
| + self._check_core_dump_pattern(fatal=True) |
| + |
| + coredumps = self._find_coredumps() |
| + if coredumps: |
| + # If we get a ton of crashes, only archive 10 dumps. |
| + archive_coredumps = coredumps[:10] |
|
Bill Hesse
2017/01/24 16:01:21
Do we want to make sure the binaries are in archiv
Vyacheslav Egorov (Google)
2017/01/24 16:12:13
Nice catch Bill!
Though we changed the way this c
|
| + print 'Archiving coredumps: %s' % ', '.join(archive_coredumps) |
| + sys.stdout.flush() |
| + self._archive(archive_coredumps) |
| + self._remove_coredumps(coredumps) |
| + coredumps = self._find_coredumps() |
| + assert not coredumps |
| + |
| + def _find_coredumps(self): |
| + return glob(os.path.join(self._search_dir, 'core.*')) |
| + |
| + def _remove_coredumps(self, coredumps): |
| + for name in coredumps: |
| + os.unlink(name) |
| + |
| + def _archive(self, coredumps): |
| + gsutil = bot_utils.GSUtil() |
| + storage_path = '%s/%s/' % (self._bucket, uuid.uuid4()) |
| + gs_prefix = 'gs://%s' % storage_path |
| + http_prefix = 'https://storage.cloud.google.com/%s' % storage_path |
| + |
| + for core in coredumps: |
| + # Sanitize the name: actual cores follow 'core.%d' pattern, crashed |
| + # binaries are copied next to cores and named 'core.<binary_name>'. |
| + suffix = os.path.basename(core).split('.')[1] |
| + try: |
| + # Check if suffix is an integer - in this case it's an actual core. |
| + clean_name = 'core.%d' % int(suffix) |
| + except: |
| + # This is not a coredump but a crashed binary. |
| + clean_name = suffix |
| + |
| + tarname = '%s.tar.gz' % clean_name |
| + |
| + # Create a .tar.gz archive out of a crash folder that contains |
| + # both binary and the core dump. |
| + tar = tarfile.open(tarname, mode='w:gz') |
| + tar.add(core, arcname=clean_name) |
| + tar.close() |
|
kustermann
2017/01/18 17:12:35
Strictly speaking, we don't need .tar.gz we just n
Vyacheslav Egorov (Google)
2017/01/18 19:57:00
Acknowledged.
|
| + |
| + # Remove / from absolute path to not have // in gs path. |
| + gs_url = '%s%s' % (gs_prefix, tarname.lstrip('/')) |
| + http_url = '%s%s' % (http_prefix, tarname.lstrip('/')) |
|
kustermann
2017/01/18 17:12:35
How could [tarname] ever contain a '/' ?
Vyacheslav Egorov (Google)
2017/01/18 19:57:00
Now it can't - it used to use an absolute path.
|
| + |
| + try: |
| + gsutil.upload(tarname, gs_url) |
| + print '@@@STEP_LOG_LINE@coredumps@%s (%s)@@@' % (gs_url, http_url) |
| + except Exception as error: |
| + message = "Failed to upload coredump %s, error: %s" % (tarname, error) |
| + print '@@@STEP_LOG_LINE@coredumps@%s@@@' % message |
| + |
| + os.unlink(tarname) |
| + |
| + print '@@@STEP_LOG_END@coredumps@@@' |
| + MarkCurrentStepWarning() |
| + |
| + 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 MarkCurrentStepWarning(): |
| + print "@@@STEP_WARNINGS@@@" |
| + sys.stdout.flush() |
| if __name__ == "__main__": |
| import sys |