Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Side by Side Diff: build/android/debug_info.py

Issue 10693110: [android] Split top-level scripts and libraries from build/android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Copyright on __init__.py, removed #! on some pylib/ files. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/android/cmd_helper.py ('k') | build/android/device_status_check.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Collect debug info for a test."""
6
7 import datetime
8 import logging
9 import os
10 import re
11 import shutil
12 import string
13 import subprocess
14 import tempfile
15
16 import cmd_helper
17
18
19 TOMBSTONE_DIR = '/data/tombstones/'
20
21
22 class GTestDebugInfo(object):
23 """A helper class to get relate debug information for a gtest.
24
25 Args:
26 adb: ADB interface the tests are using.
27 device: Serial# of the Android device in which the specified gtest runs.
28 testsuite_name: Name of the specified gtest.
29 gtest_filter: Test filter used by the specified gtest.
30 """
31
32 def __init__(self, adb, device, testsuite_name, gtest_filter,
33 collect_new_crashes=True):
34 """Initializes the DebugInfo class for a specified gtest."""
35 self.adb = adb
36 self.device = device
37 self.testsuite_name = testsuite_name
38 self.gtest_filter = gtest_filter
39 self.logcat_process = None
40 self.has_storage = False
41 self.log_dir = None
42 self.log_file_name = None
43 self.collect_new_crashes = collect_new_crashes
44 self.old_crash_files = self.ListCrashFiles()
45
46 def InitStorage(self):
47 """Initializes the storage in where we put the debug information."""
48 if self.has_storage:
49 return
50 self.has_storage = True
51 self.log_dir = tempfile.mkdtemp()
52 self.log_file_name = os.path.join(self.log_dir,
53 self._GeneratePrefixName() + '_log.txt')
54
55 def CleanupStorage(self):
56 """Cleans up the storage in where we put the debug information."""
57 if not self.has_storage:
58 return
59 self.has_storage = False
60 assert os.path.exists(self.log_dir)
61 shutil.rmtree(self.log_dir)
62 self.log_dir = None
63 self.log_file_name = None
64
65 def GetStoragePath(self):
66 """Returns the path in where we store the debug information."""
67 self.InitStorage()
68 return self.log_dir
69
70 def _GetSignatureFromGTestFilter(self):
71 """Gets a signature from gtest_filter.
72
73 Signature is used to identify the tests from which we collect debug
74 information.
75
76 Returns:
77 A signature string. Returns 'all' if there is no gtest filter.
78 """
79 if not self.gtest_filter:
80 return 'all'
81 filename_chars = "-_()%s%s" % (string.ascii_letters, string.digits)
82 return ''.join(c for c in self.gtest_filter if c in filename_chars)
83
84 def _GeneratePrefixName(self):
85 """Generates a prefix name for debug information of the test.
86
87 The prefix name consists of the following:
88 (1) root name of test_suite_base.
89 (2) device serial number.
90 (3) filter signature generate from gtest_filter.
91 (4) date & time when calling this method.
92
93 Returns:
94 Name of the log file.
95 """
96 return (os.path.splitext(self.testsuite_name)[0] + '_' + self.device + '_' +
97 self._GetSignatureFromGTestFilter() + '_' +
98 datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S-%f'))
99
100 def StartRecordingLog(self, clear=True, filters=['*:v']):
101 """Starts recording logcat output to a file.
102
103 This call should come before running test, with calling StopRecordingLog
104 following the tests.
105
106 Args:
107 clear: True if existing log output should be cleared.
108 filters: A list of logcat filters to be used.
109 """
110 self.InitStorage()
111 self.StopRecordingLog()
112 if clear:
113 cmd_helper.RunCmd(['adb', 'logcat', '-c'])
114 logging.info('Start dumping log to %s ...' % self.log_file_name)
115 command = 'adb logcat -v threadtime %s > %s' % (' '.join(filters),
116 self.log_file_name)
117 self.logcat_process = subprocess.Popen(command, shell=True)
118
119 def StopRecordingLog(self):
120 """Stops an existing logcat recording subprocess."""
121 if not self.logcat_process:
122 return
123 # Cannot evaluate directly as 0 is a possible value.
124 if self.logcat_process.poll() is None:
125 self.logcat_process.kill()
126 self.logcat_process = None
127 logging.info('Finish log dump.')
128
129 def TakeScreenshot(self, identifier_mark):
130 """Takes a screen shot from current specified device.
131
132 Args:
133 identifier_mark: A string to identify the screen shot DebugInfo will take.
134 It will be part of filename of the screen shot. Empty
135 string is acceptable.
136 Returns:
137 Returns True if successfully taking screen shot from device, otherwise
138 returns False.
139 """
140 self.InitStorage()
141 assert isinstance(identifier_mark, str)
142 shot_path = os.path.join(self.log_dir, ''.join([self._GeneratePrefixName(),
143 identifier_mark,
144 '_screenshot.png']))
145 screenshot_path = os.path.join(os.getenv('ANDROID_HOST_OUT'), 'bin',
146 'screenshot2')
147 re_success = re.compile(re.escape('Success.'), re.MULTILINE)
148 if re_success.findall(cmd_helper.GetCmdOutput([screenshot_path, '-s',
149 self.device, shot_path])):
150 logging.info("Successfully took a screen shot to %s" % shot_path)
151 return True
152 logging.error('Failed to take screen shot from device %s' % self.device)
153 return False
154
155 def ListCrashFiles(self):
156 """Collects crash files from current specified device.
157
158 Returns:
159 A dict of crash files in format {"name": (size, lastmod), ...}.
160 """
161 if not self.collect_new_crashes:
162 return {}
163 return self.adb.ListPathContents(TOMBSTONE_DIR)
164
165 def ArchiveNewCrashFiles(self):
166 """Archives the crash files newly generated until calling this method."""
167 if not self.collect_new_crashes:
168 return
169 current_crash_files = self.ListCrashFiles()
170 files = [f for f in current_crash_files if f not in self.old_crash_files]
171 logging.info('New crash file(s):%s' % ' '.join(files))
172 for f in files:
173 self.adb.Adb().Pull(TOMBSTONE_DIR + f,
174 os.path.join(self.GetStoragePath(), f))
175
176 @staticmethod
177 def ZipAndCleanResults(dest_dir, dump_file_name, debug_info_list):
178 """A helper method to zip all debug information results into a dump file.
179
180 Args:
181 dest-dir: Dir path in where we put the dump file.
182 dump_file_name: Desired name of the dump file. This method makes sure
183 '.zip' will be added as ext name.
184 debug_info_list: List of all debug info objects.
185 """
186 if not dest_dir or not dump_file_name or not debug_info_list:
187 return
188 cmd_helper.RunCmd(['mkdir', '-p', dest_dir])
189 log_basename = os.path.basename(dump_file_name)
190 log_file = os.path.join(dest_dir,
191 os.path.splitext(log_basename)[0] + '.zip')
192 logging.info('Zipping debug dumps into %s ...' % log_file)
193 for d in debug_info_list:
194 d.ArchiveNewCrashFiles()
195 # Add new dumps into the zip file. The zip may exist already if previous
196 # gtest also dumps the debug information. It's OK since we clean up the old
197 # dumps in each build step.
198 cmd_helper.RunCmd(['zip', '-q', '-r', log_file,
199 ' '.join([d.GetStoragePath() for d in debug_info_list])])
200 assert os.path.exists(log_file)
201 for debug_info in debug_info_list:
202 debug_info.CleanupStorage()
OLDNEW
« no previous file with comments | « build/android/cmd_helper.py ('k') | build/android/device_status_check.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698