OLD | NEW |
---|---|
1 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 # for details. All rights reserved. Use of this source code is governed by a | 2 # for details. All rights reserved. Use of this source code is governed by a |
3 # BSD-style license that can be found in the LICENSE file. | 3 # BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 # This file contains a set of utilities functions used by other Python-based | 5 # This file contains a set of utilities functions used by other Python-based |
6 # scripts. | 6 # scripts. |
7 | 7 |
8 import commands | 8 import commands |
9 import datetime | 9 import datetime |
10 import json | 10 import json |
11 import os | 11 import os |
12 import platform | 12 import platform |
13 import re | 13 import re |
14 import shutil | 14 import shutil |
15 import subprocess | 15 import subprocess |
16 import tempfile | 16 import tempfile |
17 import sys | 17 import sys |
18 import uuid | |
19 | |
20 try: | |
21 import resource | |
22 import tarfile | |
23 from bots import bot_utils | |
24 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.
| |
25 except: | |
26 pass | |
18 | 27 |
19 class Version(object): | 28 class Version(object): |
20 def __init__(self, channel, major, minor, patch, prerelease, | 29 def __init__(self, channel, major, minor, patch, prerelease, |
21 prerelease_patch): | 30 prerelease_patch): |
22 self.channel = channel | 31 self.channel = channel |
23 self.major = major | 32 self.major = major |
24 self.minor = minor | 33 self.minor = minor |
25 self.patch = patch | 34 self.patch = patch |
26 self.prerelease = prerelease | 35 self.prerelease = prerelease |
27 self.prerelease_patch = prerelease_patch | 36 self.prerelease_patch = prerelease_patch |
(...skipping 622 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 | 659 |
651 def __enter__(self): | 660 def __enter__(self): |
652 self._old_cwd = os.getcwd() | 661 self._old_cwd = os.getcwd() |
653 print "Enter directory = ", self._working_directory | 662 print "Enter directory = ", self._working_directory |
654 os.chdir(self._working_directory) | 663 os.chdir(self._working_directory) |
655 | 664 |
656 def __exit__(self, *_): | 665 def __exit__(self, *_): |
657 print "Enter directory = ", self._old_cwd | 666 print "Enter directory = ", self._old_cwd |
658 os.chdir(self._old_cwd) | 667 os.chdir(self._old_cwd) |
659 | 668 |
669 # This class finds and archives all core.* files from the current working | |
670 # 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.
| |
671 # the current working directory. | |
672 class CoreDumpArchiver(object): | |
673 def __init__(self, args): | |
674 self._enabled = '--copy-coredumps' in args and GuessOS() == 'linux' | |
675 self._search_dir = os.getcwd() | |
676 self._bucket = 'dart-temp-crash-archive' | |
677 self._old_limits = None | |
678 | |
679 def __enter__(self): | |
680 if not self._enabled: | |
681 return | |
682 | |
683 # Cleanup any stale coredumps | |
684 coredumps = self._find_coredumps() | |
685 if coredumps: | |
686 print "WARNING: Found stale coredumps, removing" | |
687 MarkCurrentStepWarning() | |
688 self._remove_coredumps(coredumps) | |
689 | |
690 self._old_limits = resource.getrlimit(resource.RLIMIT_CORE) | |
691 | |
692 # Bump core limits to unlimited if core_pattern is correctly configured. | |
693 if self._check_core_dump_pattern(fatal=False): | |
694 resource.setrlimit(resource.RLIMIT_CORE, (-1, -1)) | |
695 | |
696 def __exit__(self, *_): | |
697 if not self._enabled: | |
698 return | |
699 | |
700 # Restore old core limit. | |
701 resource.setrlimit(resource.RLIMIT_CORE, self._old_limits) | |
702 | |
703 # Check that kernel was correctly configured to use core.%p | |
704 # core_pattern. | |
705 self._check_core_dump_pattern(fatal=True) | |
706 | |
707 coredumps = self._find_coredumps() | |
708 if coredumps: | |
709 # If we get a ton of crashes, only archive 10 dumps. | |
710 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
| |
711 print 'Archiving coredumps: %s' % ', '.join(archive_coredumps) | |
712 sys.stdout.flush() | |
713 self._archive(archive_coredumps) | |
714 self._remove_coredumps(coredumps) | |
715 coredumps = self._find_coredumps() | |
716 assert not coredumps | |
717 | |
718 def _find_coredumps(self): | |
719 return glob(os.path.join(self._search_dir, 'core.*')) | |
720 | |
721 def _remove_coredumps(self, coredumps): | |
722 for name in coredumps: | |
723 os.unlink(name) | |
724 | |
725 def _archive(self, coredumps): | |
726 gsutil = bot_utils.GSUtil() | |
727 storage_path = '%s/%s/' % (self._bucket, uuid.uuid4()) | |
728 gs_prefix = 'gs://%s' % storage_path | |
729 http_prefix = 'https://storage.cloud.google.com/%s' % storage_path | |
730 | |
731 for core in coredumps: | |
732 # Sanitize the name: actual cores follow 'core.%d' pattern, crashed | |
733 # binaries are copied next to cores and named 'core.<binary_name>'. | |
734 suffix = os.path.basename(core).split('.')[1] | |
735 try: | |
736 # Check if suffix is an integer - in this case it's an actual core. | |
737 clean_name = 'core.%d' % int(suffix) | |
738 except: | |
739 # This is not a coredump but a crashed binary. | |
740 clean_name = suffix | |
741 | |
742 tarname = '%s.tar.gz' % clean_name | |
743 | |
744 # Create a .tar.gz archive out of a crash folder that contains | |
745 # both binary and the core dump. | |
746 tar = tarfile.open(tarname, mode='w:gz') | |
747 tar.add(core, arcname=clean_name) | |
748 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.
| |
749 | |
750 # Remove / from absolute path to not have // in gs path. | |
751 gs_url = '%s%s' % (gs_prefix, tarname.lstrip('/')) | |
752 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.
| |
753 | |
754 try: | |
755 gsutil.upload(tarname, gs_url) | |
756 print '@@@STEP_LOG_LINE@coredumps@%s (%s)@@@' % (gs_url, http_url) | |
757 except Exception as error: | |
758 message = "Failed to upload coredump %s, error: %s" % (tarname, error) | |
759 print '@@@STEP_LOG_LINE@coredumps@%s@@@' % message | |
760 | |
761 os.unlink(tarname) | |
762 | |
763 print '@@@STEP_LOG_END@coredumps@@@' | |
764 MarkCurrentStepWarning() | |
765 | |
766 def _check_core_dump_pattern(self, fatal=False): | |
767 core_pattern_file = '/proc/sys/kernel/core_pattern' | |
768 core_pattern = open(core_pattern_file).read() | |
769 | |
770 expected_core_pattern = 'core.%p' | |
771 if core_pattern.strip() != expected_core_pattern: | |
772 if fatal: | |
773 message = ("Invalid core_pattern configuration. " | |
774 "The configuration of core dump handling is *not* correct for " | |
775 "a buildbot. The content of {0} must be '{1}' instead of '{2}'." | |
776 .format(core_pattern_file, expected_core_pattern, core_pattern)) | |
777 raise Exception(message) | |
778 else: | |
779 return False | |
780 return True | |
781 | |
782 def MarkCurrentStepWarning(): | |
783 print "@@@STEP_WARNINGS@@@" | |
784 sys.stdout.flush() | |
660 | 785 |
661 if __name__ == "__main__": | 786 if __name__ == "__main__": |
662 import sys | 787 import sys |
663 Main() | 788 Main() |
OLD | NEW |