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