Index: tools/utils.py |
diff --git a/tools/utils.py b/tools/utils.py |
index cf94961e5165843655b8d7b2e103605bf103f1cf..87ee9075e51f6f8cb749008076bab3a14717bcd5 100644 |
--- a/tools/utils.py |
+++ b/tools/utils.py |
@@ -674,10 +674,21 @@ 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 |
-# the current working directory (see tools/testing/dart/test_progress.dart). |
+class CoreDump(object): |
+ def __init__(self, test, core, binary): |
+ self.test = test |
+ self.core = core |
+ self.binary = binary |
+ |
+ def __str__(self): |
+ return "%s: %s %s" % (self.test, self.binary, self.core) |
+ |
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() |
@@ -689,11 +700,8 @@ class CoreDumpArchiver(object): |
return |
# Cleanup any stale coredumps |
- coredumps = self._find_coredumps() |
- if coredumps: |
- print "WARNING: Found stale coredumps, removing" |
- MarkCurrentStepWarning() |
- self._remove_coredumps(coredumps) |
+ if self._cleanup(): |
+ print "WARNING: Found and removed stale coredumps" |
self._old_limits = resource.getrlimit(resource.RLIMIT_CORE) |
@@ -705,55 +713,83 @@ class CoreDumpArchiver(object): |
if not self._enabled: |
return |
- # Restore old core limit. |
- resource.setrlimit(resource.RLIMIT_CORE, self._old_limits) |
+ try: |
+ # 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] |
+ print 'Archiving coredumps:' |
+ for core in archive_coredumps: |
+ print '----> %s' % core |
+ |
+ sys.stdout.flush() |
+ self._archive(archive_coredumps) |
- # Check that kernel was correctly configured to use core.%p |
- # core_pattern. |
- self._check_core_dump_pattern(fatal=True) |
+ finally: |
+ self._cleanup() |
+ |
+ 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) |
+ try: |
+ os.unlink(os.path.join(self._search_dir, 'coredumps')) |
+ found = True |
+ except: |
+ pass |
- coredumps = self._find_coredumps() |
- if coredumps: |
- # If we get a ton of crashes, only archive 10 dumps. |
- archive_coredumps = coredumps[:10] |
- 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 |
+ return found |
def _find_coredumps(self): |
- return glob.glob(os.path.join(self._search_dir, 'core.*')) |
+ """Load coredumps file. Each line has the following format: |
- def _remove_coredumps(self, coredumps): |
- for name in coredumps: |
- os.unlink(name) |
+ test-name,core-file,binary-file |
+ """ |
+ try: |
+ with open('coredumps') as f: |
+ return [CoreDump(*ln.strip('\n').split(',')) for ln in f.readlines()] |
+ except: |
+ return [] |
def _archive(self, coredumps): |
+ files = set() |
+ for core in coredumps: |
+ files.add(core.core) |
+ files.add(core.binary) |
+ self._upload(files) |
+ |
+ def _upload(self, files): |
bot_utils = GetBotUtils() |
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: |
+ print '\n--- Uploading into %s (%s) ---' % (gs_prefix, http_prefix) |
+ for file in files: |
# 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 |
+ # binaries are copied next to cores and named 'binary.<binary_name>'. |
+ name = os.path.basename(file) |
+ (prefix, suffix) = name.split('.', 1) |
+ if prefix == 'binary': |
+ name = suffix |
- tarname = '%s.tar.gz' % clean_name |
+ tarname = '%s.tar.gz' % name |
- # Create a .tar.gz archive out of a crash folder that contains |
- # both binary and the core dump. |
+ # Compress the file. |
tar = tarfile.open(tarname, mode='w:gz') |
- tar.add(core, arcname=clean_name) |
+ tar.add(file, arcname=name) |
tar.close() |
# Remove / from absolute path to not have // in gs path. |
@@ -762,15 +798,12 @@ class CoreDumpArchiver(object): |
try: |
gsutil.upload(tarname, gs_url) |
- print '@@@STEP_LOG_LINE@coredumps@%s (%s)@@@' % (gs_url, http_url) |
+ print '+++ Uploaded %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 |
+ print '!!! Failed to upload %s, error: %s' % (tarname, error) |
os.unlink(tarname) |
- |
- print '@@@STEP_LOG_END@coredumps@@@' |
- MarkCurrentStepWarning() |
+ print '--- Done ---\n' |
def _check_core_dump_pattern(self, fatal=False): |
core_pattern_file = '/proc/sys/kernel/core_pattern' |
@@ -779,19 +812,15 @@ class CoreDumpArchiver(object): |
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}'." |
+ 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 |
Main() |