Index: build/android/pylib/debug_info.py |
diff --git a/build/android/pylib/debug_info.py b/build/android/pylib/debug_info.py |
index 4678c20d9e767070dbb9b8289cc5761d2bbb2d28..6f0f55a33f2eafa74dd38d4b5f9625aaf5ce2cc3 100644 |
--- a/build/android/pylib/debug_info.py |
+++ b/build/android/pylib/debug_info.py |
@@ -20,7 +20,13 @@ TOMBSTONE_DIR = '/data/tombstones/' |
class GTestDebugInfo(object): |
- """A helper class to get relate debug information for a gtest. |
+ """A helper class to collect related debug information for a gtest. |
+ |
+ Debug info is collected in two steps: |
+ - first, object(s) of this class (one per device), accumulate logs |
+ and screenshots in tempdir. |
+ - once the test has finished, call ZipAndCleanResults to create |
+ a zip containing the logs from all devices, and clean them up. |
Args: |
adb: ADB interface the tests are using. |
@@ -29,8 +35,7 @@ class GTestDebugInfo(object): |
gtest_filter: Test filter used by the specified gtest. |
""" |
- def __init__(self, adb, device, testsuite_name, gtest_filter, |
- collect_new_crashes=True): |
+ def __init__(self, adb, device, testsuite_name, gtest_filter): |
"""Initializes the DebugInfo class for a specified gtest.""" |
self.adb = adb |
self.device = device |
@@ -38,34 +43,15 @@ class GTestDebugInfo(object): |
self.gtest_filter = gtest_filter |
self.logcat_process = None |
self.has_storage = False |
- self.log_dir = None |
- self.log_file_name = None |
- self.collect_new_crashes = collect_new_crashes |
- self.old_crash_files = self.ListCrashFiles() |
- |
- def InitStorage(self): |
- """Initializes the storage in where we put the debug information.""" |
- if self.has_storage: |
- return |
- self.has_storage = True |
- self.log_dir = tempfile.mkdtemp() |
+ self.log_dir = os.path.join(tempfile.gettempdir(), |
+ 'gtest_debug_info', |
+ self.testsuite_name, |
+ self.device) |
+ if not os.path.exists(self.log_dir): |
+ os.makedirs(self.log_dir) |
self.log_file_name = os.path.join(self.log_dir, |
self._GeneratePrefixName() + '_log.txt') |
- |
- def CleanupStorage(self): |
- """Cleans up the storage in where we put the debug information.""" |
- if not self.has_storage: |
- return |
- self.has_storage = False |
- assert os.path.exists(self.log_dir) |
- shutil.rmtree(self.log_dir) |
- self.log_dir = None |
- self.log_file_name = None |
- |
- def GetStoragePath(self): |
- """Returns the path in where we store the debug information.""" |
- self.InitStorage() |
- return self.log_dir |
+ self.old_crash_files = self._ListCrashFiles() |
def _GetSignatureFromGTestFilter(self): |
"""Gets a signature from gtest_filter. |
@@ -79,7 +65,11 @@ class GTestDebugInfo(object): |
if not self.gtest_filter: |
return 'all' |
filename_chars = "-_()%s%s" % (string.ascii_letters, string.digits) |
- return ''.join(c for c in self.gtest_filter if c in filename_chars) |
+ signature = ''.join(c for c in self.gtest_filter if c in filename_chars) |
+ if len(signature) > 64: |
+ # The signature can't be too long, as it'll be part of a file name. |
+ signature = signature[:64] |
+ return signature |
def _GeneratePrefixName(self): |
"""Generates a prefix name for debug information of the test. |
@@ -87,7 +77,7 @@ class GTestDebugInfo(object): |
The prefix name consists of the following: |
(1) root name of test_suite_base. |
(2) device serial number. |
- (3) filter signature generate from gtest_filter. |
+ (3) prefix of filter signature generate from gtest_filter. |
(4) date & time when calling this method. |
Returns: |
@@ -107,13 +97,13 @@ class GTestDebugInfo(object): |
clear: True if existing log output should be cleared. |
filters: A list of logcat filters to be used. |
""" |
- self.InitStorage() |
self.StopRecordingLog() |
if clear: |
- cmd_helper.RunCmd(['adb', 'logcat', '-c']) |
- logging.info('Start dumping log to %s ...' % self.log_file_name) |
- command = 'adb logcat -v threadtime %s > %s' % (' '.join(filters), |
- self.log_file_name) |
+ cmd_helper.RunCmd(['adb', '-s', self.device, 'logcat', '-c']) |
+ logging.info('Start dumping log to %s ...', self.log_file_name) |
+ command = 'adb -s %s logcat -v threadtime %s > %s' % (self.device, |
+ ' '.join(filters), |
+ self.log_file_name) |
self.logcat_process = subprocess.Popen(command, shell=True) |
def StopRecordingLog(self): |
@@ -137,74 +127,70 @@ class GTestDebugInfo(object): |
Returns the file name on the host of the screenshot if successful, |
None otherwise. |
""" |
- self.InitStorage() |
assert isinstance(identifier_mark, str) |
+ screenshot_path = os.path.join(os.getenv('ANDROID_HOST_OUT', ''), |
+ 'bin', |
+ 'screenshot2') |
+ if not os.path.exists(screenshot_path): |
+ logging.error('Failed to take screen shot from device %s', self.device) |
+ return None |
shot_path = os.path.join(self.log_dir, ''.join([self._GeneratePrefixName(), |
identifier_mark, |
'_screenshot.png'])) |
- screenshot_path = os.path.join(os.getenv('ANDROID_HOST_OUT'), 'bin', |
- 'screenshot2') |
re_success = re.compile(re.escape('Success.'), re.MULTILINE) |
if re_success.findall(cmd_helper.GetCmdOutput([screenshot_path, '-s', |
self.device, shot_path])): |
- logging.info("Successfully took a screen shot to %s" % shot_path) |
+ logging.info('Successfully took a screen shot to %s', shot_path) |
return shot_path |
- logging.error('Failed to take screen shot from device %s' % self.device) |
+ logging.error('Failed to take screen shot from device %s', self.device) |
return None |
- def ListCrashFiles(self): |
+ def _ListCrashFiles(self): |
"""Collects crash files from current specified device. |
Returns: |
A dict of crash files in format {"name": (size, lastmod), ...}. |
""" |
- if not self.collect_new_crashes: |
- return {} |
return self.adb.ListPathContents(TOMBSTONE_DIR) |
def ArchiveNewCrashFiles(self): |
"""Archives the crash files newly generated until calling this method.""" |
- if not self.collect_new_crashes: |
- return |
- current_crash_files = self.ListCrashFiles() |
+ current_crash_files = self._ListCrashFiles() |
files = [] |
for f in current_crash_files: |
if f not in self.old_crash_files: |
files += [f] |
elif current_crash_files[f] != self.old_crash_files[f]: |
- # Tomestones dir can only have maximum 10 files, so we need to compare |
+ # Tombstones dir can only have maximum 10 files, so we need to compare |
# size and timestamp information of file if the file exists. |
files += [f] |
if files: |
logging.info('New crash file(s):%s' % ' '.join(files)) |
for f in files: |
self.adb.Adb().Pull(TOMBSTONE_DIR + f, |
- os.path.join(self.GetStoragePath(), f)) |
+ os.path.join(self.log_dir, f)) |
@staticmethod |
- def ZipAndCleanResults(dest_dir, dump_file_name, debug_info_list): |
+ def ZipAndCleanResults(dest_dir, dump_file_name): |
"""A helper method to zip all debug information results into a dump file. |
Args: |
- dest-dir: Dir path in where we put the dump file. |
+ dest_dir: Dir path in where we put the dump file. |
dump_file_name: Desired name of the dump file. This method makes sure |
'.zip' will be added as ext name. |
- debug_info_list: List of all debug info objects. |
""" |
- if not dest_dir or not dump_file_name or not debug_info_list: |
+ if not dest_dir or not dump_file_name: |
return |
cmd_helper.RunCmd(['mkdir', '-p', dest_dir]) |
log_basename = os.path.basename(dump_file_name) |
- log_file = os.path.join(dest_dir, |
- os.path.splitext(log_basename)[0] + '.zip') |
- logging.info('Zipping debug dumps into %s ...' % log_file) |
- for d in debug_info_list: |
- d.ArchiveNewCrashFiles() |
+ log_zip_file = os.path.join(dest_dir, |
+ os.path.splitext(log_basename)[0] + '.zip') |
+ logging.info('Zipping debug dumps into %s ...', log_zip_file) |
# Add new dumps into the zip file. The zip may exist already if previous |
# gtest also dumps the debug information. It's OK since we clean up the old |
# dumps in each build step. |
- cmd_helper.RunCmd(['zip', '-q', '-r', log_file, |
- ' '.join([d.GetStoragePath() for d in debug_info_list])]) |
- assert os.path.exists(log_file) |
- for debug_info in debug_info_list: |
- debug_info.CleanupStorage() |
+ log_src_dir = os.path.join(tempfile.gettempdir(), 'gtest_debug_info') |
+ cmd_helper.RunCmd(['zip', '-q', '-r', log_zip_file, log_src_dir]) |
+ assert os.path.exists(log_zip_file) |
+ assert os.path.exists(log_src_dir) |
+ shutil.rmtree(log_src_dir) |