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 glob | 10 import glob |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
667 | 667 |
668 def __enter__(self): | 668 def __enter__(self): |
669 self._old_cwd = os.getcwd() | 669 self._old_cwd = os.getcwd() |
670 print "Enter directory = ", self._working_directory | 670 print "Enter directory = ", self._working_directory |
671 os.chdir(self._working_directory) | 671 os.chdir(self._working_directory) |
672 | 672 |
673 def __exit__(self, *_): | 673 def __exit__(self, *_): |
674 print "Enter directory = ", self._old_cwd | 674 print "Enter directory = ", self._old_cwd |
675 os.chdir(self._old_cwd) | 675 os.chdir(self._old_cwd) |
676 | 676 |
677 # This class finds and archives all core.* files from the current working | 677 class CoreDump(object): |
678 # directory and all binaries copied by UnexpectedCrashDumpArchiver into | 678 def __init__(self, test, core, binary): |
679 # the current working directory (see tools/testing/dart/test_progress.dart). | 679 self.test = test |
| 680 self.core = core |
| 681 self.binary = binary |
| 682 |
| 683 def __str__(self): |
| 684 return "%s: %s %s" % (self.test, self.binary, self.core) |
| 685 |
680 class CoreDumpArchiver(object): | 686 class CoreDumpArchiver(object): |
| 687 """This class reads coredumps file written by UnexpectedCrashDumpArchiver |
| 688 into the current working directory and uploads all cores and binaries |
| 689 listed in it into Cloud Storage (see tools/testing/dart/test_progress.dart). |
| 690 """ |
| 691 |
681 def __init__(self, args): | 692 def __init__(self, args): |
682 self._enabled = '--copy-coredumps' in args and GuessOS() == 'linux' | 693 self._enabled = '--copy-coredumps' in args and GuessOS() == 'linux' |
683 self._search_dir = os.getcwd() | 694 self._search_dir = os.getcwd() |
684 self._bucket = 'dart-temp-crash-archive' | 695 self._bucket = 'dart-temp-crash-archive' |
685 self._old_limits = None | 696 self._old_limits = None |
686 | 697 |
687 def __enter__(self): | 698 def __enter__(self): |
688 if not self._enabled: | 699 if not self._enabled: |
689 return | 700 return |
690 | 701 |
691 # Cleanup any stale coredumps | 702 # Cleanup any stale coredumps |
692 coredumps = self._find_coredumps() | 703 if self._cleanup(): |
693 if coredumps: | 704 print "WARNING: Found and removed stale coredumps" |
694 print "WARNING: Found stale coredumps, removing" | |
695 MarkCurrentStepWarning() | |
696 self._remove_coredumps(coredumps) | |
697 | 705 |
698 self._old_limits = resource.getrlimit(resource.RLIMIT_CORE) | 706 self._old_limits = resource.getrlimit(resource.RLIMIT_CORE) |
699 | 707 |
700 # Bump core limits to unlimited if core_pattern is correctly configured. | 708 # Bump core limits to unlimited if core_pattern is correctly configured. |
701 if self._check_core_dump_pattern(fatal=False): | 709 if self._check_core_dump_pattern(fatal=False): |
702 resource.setrlimit(resource.RLIMIT_CORE, (-1, -1)) | 710 resource.setrlimit(resource.RLIMIT_CORE, (-1, -1)) |
703 | 711 |
704 def __exit__(self, *_): | 712 def __exit__(self, *_): |
705 if not self._enabled: | 713 if not self._enabled: |
706 return | 714 return |
707 | 715 |
708 # Restore old core limit. | 716 try: |
709 resource.setrlimit(resource.RLIMIT_CORE, self._old_limits) | 717 # Restore old core limit. |
| 718 resource.setrlimit(resource.RLIMIT_CORE, self._old_limits) |
710 | 719 |
711 # Check that kernel was correctly configured to use core.%p | 720 # Check that kernel was correctly configured to use core.%p |
712 # core_pattern. | 721 # core_pattern. |
713 self._check_core_dump_pattern(fatal=True) | 722 self._check_core_dump_pattern(fatal=True) |
714 | 723 |
715 coredumps = self._find_coredumps() | 724 coredumps = self._find_coredumps() |
716 if coredumps: | 725 if coredumps: |
717 # If we get a ton of crashes, only archive 10 dumps. | 726 # If we get a ton of crashes, only archive 10 dumps. |
718 archive_coredumps = coredumps[:10] | 727 archive_coredumps = coredumps[:10] |
719 print 'Archiving coredumps: %s' % ', '.join(archive_coredumps) | 728 print 'Archiving coredumps:' |
720 sys.stdout.flush() | 729 for core in archive_coredumps: |
721 self._archive(archive_coredumps) | 730 print '----> %s' % core |
722 self._remove_coredumps(coredumps) | 731 |
723 coredumps = self._find_coredumps() | 732 sys.stdout.flush() |
724 assert not coredumps | 733 self._archive(archive_coredumps) |
| 734 |
| 735 finally: |
| 736 self._cleanup() |
| 737 |
| 738 def _cleanup(self): |
| 739 found = False |
| 740 for core in glob.glob(os.path.join(self._search_dir, 'core.*')): |
| 741 found = True |
| 742 os.unlink(core) |
| 743 for binary in glob.glob(os.path.join(self._search_dir, 'binary.*')): |
| 744 found = True |
| 745 os.unlink(binary) |
| 746 try: |
| 747 os.unlink(os.path.join(self._search_dir, 'coredumps')) |
| 748 found = True |
| 749 except: |
| 750 pass |
| 751 |
| 752 return found |
725 | 753 |
726 def _find_coredumps(self): | 754 def _find_coredumps(self): |
727 return glob.glob(os.path.join(self._search_dir, 'core.*')) | 755 """Load coredumps file. Each line has the following format: |
728 | 756 |
729 def _remove_coredumps(self, coredumps): | 757 test-name,core-file,binary-file |
730 for name in coredumps: | 758 """ |
731 os.unlink(name) | 759 try: |
| 760 with open('coredumps') as f: |
| 761 return [CoreDump(*ln.strip('\n').split(',')) for ln in f.readlines()] |
| 762 except: |
| 763 return [] |
732 | 764 |
733 def _archive(self, coredumps): | 765 def _archive(self, coredumps): |
| 766 files = set() |
| 767 for core in coredumps: |
| 768 files.add(core.core) |
| 769 files.add(core.binary) |
| 770 self._upload(files) |
| 771 |
| 772 def _upload(self, files): |
734 bot_utils = GetBotUtils() | 773 bot_utils = GetBotUtils() |
735 gsutil = bot_utils.GSUtil() | 774 gsutil = bot_utils.GSUtil() |
736 storage_path = '%s/%s/' % (self._bucket, uuid.uuid4()) | 775 storage_path = '%s/%s/' % (self._bucket, uuid.uuid4()) |
737 gs_prefix = 'gs://%s' % storage_path | 776 gs_prefix = 'gs://%s' % storage_path |
738 http_prefix = 'https://storage.cloud.google.com/%s' % storage_path | 777 http_prefix = 'https://storage.cloud.google.com/%s' % storage_path |
739 | 778 |
740 for core in coredumps: | 779 print '\n--- Uploading into %s (%s) ---' % (gs_prefix, http_prefix) |
| 780 for file in files: |
741 # Sanitize the name: actual cores follow 'core.%d' pattern, crashed | 781 # Sanitize the name: actual cores follow 'core.%d' pattern, crashed |
742 # binaries are copied next to cores and named 'core.<binary_name>'. | 782 # binaries are copied next to cores and named 'binary.<binary_name>'. |
743 suffix = os.path.basename(core).split('.')[1] | 783 name = os.path.basename(file) |
744 try: | 784 (prefix, suffix) = name.split('.', 1) |
745 # Check if suffix is an integer - in this case it's an actual core. | 785 if prefix == 'binary': |
746 clean_name = 'core.%d' % int(suffix) | 786 name = suffix |
747 except: | |
748 # This is not a coredump but a crashed binary. | |
749 clean_name = suffix | |
750 | 787 |
751 tarname = '%s.tar.gz' % clean_name | 788 tarname = '%s.tar.gz' % name |
752 | 789 |
753 # Create a .tar.gz archive out of a crash folder that contains | 790 # Compress the file. |
754 # both binary and the core dump. | |
755 tar = tarfile.open(tarname, mode='w:gz') | 791 tar = tarfile.open(tarname, mode='w:gz') |
756 tar.add(core, arcname=clean_name) | 792 tar.add(file, arcname=name) |
757 tar.close() | 793 tar.close() |
758 | 794 |
759 # Remove / from absolute path to not have // in gs path. | 795 # Remove / from absolute path to not have // in gs path. |
760 gs_url = '%s%s' % (gs_prefix, tarname) | 796 gs_url = '%s%s' % (gs_prefix, tarname) |
761 http_url = '%s%s' % (http_prefix, tarname) | 797 http_url = '%s%s' % (http_prefix, tarname) |
762 | 798 |
763 try: | 799 try: |
764 gsutil.upload(tarname, gs_url) | 800 gsutil.upload(tarname, gs_url) |
765 print '@@@STEP_LOG_LINE@coredumps@%s (%s)@@@' % (gs_url, http_url) | 801 print '+++ Uploaded %s (%s)' % (gs_url, http_url) |
766 except Exception as error: | 802 except Exception as error: |
767 message = "Failed to upload coredump %s, error: %s" % (tarname, error) | 803 print '!!! Failed to upload %s, error: %s' % (tarname, error) |
768 print '@@@STEP_LOG_LINE@coredumps@%s@@@' % message | |
769 | 804 |
770 os.unlink(tarname) | 805 os.unlink(tarname) |
771 | 806 print '--- Done ---\n' |
772 print '@@@STEP_LOG_END@coredumps@@@' | |
773 MarkCurrentStepWarning() | |
774 | 807 |
775 def _check_core_dump_pattern(self, fatal=False): | 808 def _check_core_dump_pattern(self, fatal=False): |
776 core_pattern_file = '/proc/sys/kernel/core_pattern' | 809 core_pattern_file = '/proc/sys/kernel/core_pattern' |
777 core_pattern = open(core_pattern_file).read() | 810 core_pattern = open(core_pattern_file).read() |
778 | 811 |
779 expected_core_pattern = 'core.%p' | 812 expected_core_pattern = 'core.%p' |
780 if core_pattern.strip() != expected_core_pattern: | 813 if core_pattern.strip() != expected_core_pattern: |
781 if fatal: | 814 if fatal: |
782 message = ("Invalid core_pattern configuration. " | 815 message = ('Invalid core_pattern configuration. ' |
783 "The configuration of core dump handling is *not* correct for " | 816 'The configuration of core dump handling is *not* correct for ' |
784 "a buildbot. The content of {0} must be '{1}' instead of '{2}'." | 817 'a buildbot. The content of {0} must be "{1}" instead of "{2}".' |
785 .format(core_pattern_file, expected_core_pattern, core_pattern)) | 818 .format(core_pattern_file, expected_core_pattern, core_pattern)) |
786 raise Exception(message) | 819 raise Exception(message) |
787 else: | 820 else: |
788 return False | 821 return False |
789 return True | 822 return True |
790 | 823 |
791 def MarkCurrentStepWarning(): | |
792 print "@@@STEP_WARNINGS@@@" | |
793 sys.stdout.flush() | |
794 | |
795 if __name__ == "__main__": | 824 if __name__ == "__main__": |
796 import sys | 825 import sys |
797 Main() | 826 Main() |
OLD | NEW |