blob: 3cbc8c21bb38da325ba9b4ed2ec55c08d72a6de3 [file] [log] [blame]
Dennis Kempin351024d2013-02-06 11:18:21 -08001# Copyright (c) 2013 The Chromium OS 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
5from gzip import GzipFile
Dennis Kempincee37612013-06-14 10:40:41 -07006from mtlib.feedback import FeedbackDownloader
Dennis Kempin351024d2013-02-06 11:18:21 -08007from StringIO import StringIO
Dennis Kempin60571e02014-01-09 12:00:15 -08008from cros_remote import CrOSRemote
Dennis Kempin351024d2013-02-06 11:18:21 -08009import base64
10import bz2
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -040011import imghdr
Dennis Kempin351024d2013-02-06 11:18:21 -080012import os
13import os.path
14import re
15import subprocess
16import sys
17import tarfile
18import tempfile
Dennis Kempinecd9a0c2013-03-27 16:03:46 -070019import zipfile
Dennis Kempin351024d2013-02-06 11:18:21 -080020
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040021# path for temporary screenshot
Dennis Kempin17766a62013-06-17 14:09:33 -070022screenshot_filepath = 'extension/screenshot.jpg'
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040023
Dennis Kempinaa7ce272014-04-18 11:50:59 -070024class Options:
25 def __init__(self, download=False, new=False, screenshot=False,
26 evdev=None):
27 self.download = download
28 self.new = new
29 self.screenshot = screenshot
30 self.evdev = evdev
31
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070032def Log(source=None, options=None, activity=None, evdev=None):
Dennis Kempin17766a62013-06-17 14:09:33 -070033 """ Load log from feedback url, device ip or local file path.
34
35 Returns a log object containg activity, evdev and system logs.
36 source can be either an ip address to a device from which to
37 download, a url pointing to a feedback report to pull or a filename.
Dennis Kempin351024d2013-02-06 11:18:21 -080038 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -070039 if not options:
40 options = Options()
41
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070042 if source:
43 if os.path.exists(source):
44 if source.endswith('.zip') or source.endswith('.bz2'):
Dennis Kempinaa7ce272014-04-18 11:50:59 -070045 return FeedbackLog(source)
46 return FileLog(source, options.evdev)
Dennis Kempin351024d2013-02-06 11:18:21 -080047 else:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070048 match = re.search('Report/([0-9]+)', source)
49 if match:
Dennis Kempinaa7ce272014-04-18 11:50:59 -070050 return FeedbackLog(match.group(1), options.screenshot,
51 options.download)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070052 else:
53 # todo add regex and error message
Dennis Kempinaa7ce272014-04-18 11:50:59 -070054 return DeviceLog(source, options.new)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070055
56 if activity or evdev:
57 log = AbstractLog()
58 if activity:
59 if os.path.exists(activity):
60 log.activity = open(activity).read()
61 else:
62 log.activity = activity
63 if evdev:
64 if os.path.exists(evdev):
65 log.evdev = open(evdev).read()
66 else:
67 log.evdev = evdev
68 return log
Dennis Kempin351024d2013-02-06 11:18:21 -080069
70
71class AbstractLog(object):
Dennis Kempin17766a62013-06-17 14:09:33 -070072 """ Common functions for log files
73
74 A log file consists of the activity log, the evdev log, a system log, and
75 possibly a screenshot, which can be saved to disk all together.
Dennis Kempin351024d2013-02-06 11:18:21 -080076 """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070077 def __init__(self):
Dennis Kempinaa7ce272014-04-18 11:50:59 -070078 self.activity = ''
79 self.evdev = ''
80 self.system = ''
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070081 self.image = None
82
Dennis Kempin351024d2013-02-06 11:18:21 -080083 def SaveAs(self, filename):
Dennis Kempin17766a62013-06-17 14:09:33 -070084 open(filename, 'w').write(self.activity)
Dennis Kempin351024d2013-02-06 11:18:21 -080085 if self.evdev:
Dennis Kempin17766a62013-06-17 14:09:33 -070086 open(filename + '.evdev', 'w').write(self.evdev)
Dennis Kempin351024d2013-02-06 11:18:21 -080087 if self.system:
Dennis Kempin17766a62013-06-17 14:09:33 -070088 open(filename + '.system', 'w').write(self.system)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040089 if self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -070090 open(filename + '.jpg', 'w').write(self.image)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040091
92 def CleanUp(self):
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040093 if os.path.exists(screenshot_filepath):
94 os.remove(screenshot_filepath)
Dennis Kempin351024d2013-02-06 11:18:21 -080095
96
97class FileLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -070098 """ Loads log from file.
99
100 evdev or system logs are optional.
Dennis Kempin351024d2013-02-06 11:18:21 -0800101 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700102 def __init__(self, filename, evdev=None):
103 AbstractLog.__init__(self)
104
Dennis Kempin351024d2013-02-06 11:18:21 -0800105 self.activity = open(filename).read()
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700106 if evdev:
107 self.evdev = open(evdev).read()
Dennis Kempin17766a62013-06-17 14:09:33 -0700108 elif os.path.exists(filename + '.evdev'):
109 self.evdev = open(filename + '.evdev').read()
110 if os.path.exists(filename + '.system'):
111 self.system = open(filename + '.system').read()
112 if os.path.exists(filename + '.jpg'):
113 self.image = open(filename + '.jpg').read()
114 file(screenshot_filepath, 'w').write(self.image)
Dennis Kempin351024d2013-02-06 11:18:21 -0800115
116
117class FeedbackLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700118 """ Download log from feedback url.
119
120 Downloads logs and (possibly) screenshot from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -0800121 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700122 def __init__(self, id_or_filename, screenshot=False, download=False,
Sean O'Brienfd204da2017-05-02 15:13:11 -0700123 force_latest=None, downloader=None):
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700124 AbstractLog.__init__(self)
Dennis Kempinb049d542013-06-13 13:55:18 -0700125 self.force_latest = force_latest
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700126 self.try_screenshot = screenshot
Dennis Kempin19e972b2013-06-20 13:21:38 -0700127 self.report = None
Sean O'Brienfd204da2017-05-02 15:13:11 -0700128 self.downloader = downloader
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400129
Dennis Kempin17766a62013-06-17 14:09:33 -0700130 if id_or_filename.endswith('.zip') or id_or_filename.endswith('.bz2'):
Dennis Kempin91e96df2013-03-29 14:21:42 -0700131 self.report = open(id_or_filename).read()
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400132 screenshot_filename = id_or_filename[:-4] + '.jpg'
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700133 try:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400134 self.image = open(screenshot_filename).read()
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700135 except IOError:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400136 # No corresponding screenshot available.
137 pass
138 else:
Sean O'Brienfd204da2017-05-02 15:13:11 -0700139 if not self.downloader:
140 self.downloader = FeedbackDownloader()
Dennis Kempincee37612013-06-14 10:40:41 -0700141 self.report = self.downloader.DownloadSystemLog(id_or_filename)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400142 if self.try_screenshot:
Dennis Kempincee37612013-06-14 10:40:41 -0700143 self.image = self.downloader.DownloadScreenshot(id_or_filename)
Dennis Kempin351024d2013-02-06 11:18:21 -0800144
Dennis Kempin13d948e2014-04-18 11:23:32 -0700145 if self.report:
146 self._ExtractSystemLog()
147 if self.system:
148 self._ExtractLogFiles()
149
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400150 # Only write to screenshot.jpg if we will be viewing the screenshot
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700151 if not download and self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -0700152 file(screenshot_filepath, 'w').write(self.image)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400153
Dennis Kempin351024d2013-02-06 11:18:21 -0800154 def _GetLatestFile(self, match, tar):
155 # find file name containing match with latest timestamp
156 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
157 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700158 if not names:
Dennis Kempin17766a62013-06-17 14:09:33 -0700159 print 'Cannot find files named %s in tar file' % match
160 print 'Tar file contents:', tar.getnames()
Dennis Kempin765ad942013-03-26 13:58:46 -0700161 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800162 return tar.extractfile(names[-1])
163
Dennis Kempin91e96df2013-03-29 14:21:42 -0700164 def _ExtractSystemLog(self):
Dennis Kempin17766a62013-06-17 14:09:33 -0700165 if self.report[0:2] == 'BZ':
Dennis Kempin91e96df2013-03-29 14:21:42 -0700166 self.system = bz2.decompress(self.report)
Dennis Kempin17766a62013-06-17 14:09:33 -0700167 elif self.report[0:2] == 'PK':
Dennis Kempin91e96df2013-03-29 14:21:42 -0700168 io = StringIO(self.report)
Dennis Kempin17766a62013-06-17 14:09:33 -0700169 zip = zipfile.ZipFile(io, 'r')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700170 self.system = zip.read(zip.namelist()[0])
Dennis Kempin17766a62013-06-17 14:09:33 -0700171 zip.extractall('./')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700172 else:
Dennis Kempin17766a62013-06-17 14:09:33 -0700173 print 'Cannot download logs file'
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700174 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800175
176 def _ExtractLogFiles(self):
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400177 # Find embedded and uuencoded activity.tar in system log
178
179 def ExtractByInterface(interface):
180 assert interface == 'pad' or interface == 'screen'
181
182 log_index = [
Dennis Kempin19e972b2013-06-20 13:21:38 -0700183 (('hack-33025-touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700184 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
185 '"""'),
Dennis Kempin19e972b2013-06-20 13:21:38 -0700186 (('touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700187 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
188 '"""'),
189 (('hack-33025-touch{0}_activity=<multiline>\n' +
190 '---------- START ----------\n' +
191 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
192 '---------- END ----------'),
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400193 ]
194
195 start_index = end_index = None
196 for start, end in log_index:
197 if start in self.system:
198 start_index = self.system.index(start) + len(start)
199 end_index = self.system.index(end, start_index)
200 break
201
202 if start_index is None:
203 return []
204
205 activity_tar_enc = self.system[start_index:end_index]
206
207 # base64 decode
208 activity_tar_data = base64.b64decode(activity_tar_enc)
209
Sean O'Brien0d2d0c42018-02-06 09:37:42 -0800210 if activity_tar_data == '':
211 print 'touch{0}_activity_log.tar found, but empty'.format(interface)
212 return []
213
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400214 # untar
215 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
216
217 def ExtractPadFiles(name):
218 # find matching evdev file
Sean O'Brien146a6842018-07-09 09:09:40 -0700219 evdev_name = name.replace('touchpad_activity', 'cmt_input_events')
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400220
221 activity_gz = activity_tar_file.extractfile(name)
222 evdev_gz = activity_tar_file.extractfile(evdev_name)
223
224 # decompress log files
225 return (GzipFile(fileobj=activity_gz).read(),
226 GzipFile(fileobj=evdev_gz).read())
227
228 def ExtractScreenFiles(name):
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400229 # Always try for a screenshot with touchscreen view
230 self.try_screenshot = True
231
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400232 evdev = activity_tar_file.extractfile(name)
233 return (evdev.read(), None)
234
235 extract_func = {
236 'pad': ExtractPadFiles,
237 'screen': ExtractScreenFiles,
238 }
239
240 # return latest log files, we don't include cmt files
241 return [(filename, extract_func[interface])
242 for filename in activity_tar_file.getnames()
243 if filename.find('cmt') == -1]
244
Dennis Kempin17766a62013-06-17 14:09:33 -0700245 if self.force_latest == 'pad':
Dennis Kempinb049d542013-06-13 13:55:18 -0700246 logs = ExtractByInterface('pad')
247 idx = 0
Dennis Kempin17766a62013-06-17 14:09:33 -0700248 elif self.force_latest == 'screen':
Dennis Kempinb049d542013-06-13 13:55:18 -0700249 logs = ExtractByInterface('screen')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700250 idx = 0
251 else:
Dennis Kempinb049d542013-06-13 13:55:18 -0700252 logs = ExtractByInterface('pad') + ExtractByInterface('screen')
253 if not logs:
Dennis Kempin17766a62013-06-17 14:09:33 -0700254 print ('Cannot find touchpad_activity_log.tar or ' +
255 'touchscreen_activity_log.tar in systems log file.')
Dennis Kempinb049d542013-06-13 13:55:18 -0700256 sys.exit(-1)
257
258 if len(logs) == 1:
259 idx = 0
260 else:
261 while True:
Dennis Kempin17766a62013-06-17 14:09:33 -0700262 print 'Which log file would you like to use?'
Dennis Kempinb049d542013-06-13 13:55:18 -0700263 for i, (name, extract_func) in enumerate(logs):
264 if name.startswith('evdev'):
265 name = 'touchscreen log - ' + name
266 print i, name
Dennis Kempin17766a62013-06-17 14:09:33 -0700267 print '>'
Dennis Kempinb049d542013-06-13 13:55:18 -0700268 selection = sys.stdin.readline()
269 try:
270 idx = int(selection)
271 if idx < 0 or idx >= len(logs):
Dennis Kempin17766a62013-06-17 14:09:33 -0700272 print 'Number out of range'
Dennis Kempinb049d542013-06-13 13:55:18 -0700273 else:
274 break
275 except:
Dennis Kempin17766a62013-06-17 14:09:33 -0700276 print 'Not a number'
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700277
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400278 name, extract_func = logs[idx]
279 self.activity, self.evdev = extract_func(name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800280
281
282identity_message = """\
283In order to access devices in TPTool, you need to have password-less
284auth for chromebooks set up.
285Would you like tptool to run the following command for you?
286$ %s
287Yes/No? (Default: No)"""
288
289class DeviceLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700290 """ Downloads logs from a running chromebook via scp. """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700291 def __init__(self, ip, new=False):
292 AbstractLog.__init__(self)
Dennis Kempin60571e02014-01-09 12:00:15 -0800293 self.remote = CrOSRemote(ip)
Dennis Kempin351024d2013-02-06 11:18:21 -0800294
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700295 if new:
Dennis Kempin58d9a742014-05-08 18:34:59 -0700296 self.remote.SafeExecute('/opt/google/input/inputcontrol -t touchpad' +
297 ' --log')
Dennis Kempin60571e02014-01-09 12:00:15 -0800298 self.activity = self.remote.Read('/var/log/xorg/touchpad_activity_log.txt')
299 self.evdev = self.remote.Read('/var/log/xorg/cmt_input_events.dat')