blob: 552f2a2bd9db9138a3cf4b576c5ac4deb6cc157b [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 Kempin60571e02014-01-09 12:00:15 -080024# todo(denniskempin): the whole options thing is ugly when accessed
25# programmatically without OptionParser.
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070026def Log(source=None, options=None, activity=None, evdev=None):
Dennis Kempin17766a62013-06-17 14:09:33 -070027 """ Load log from feedback url, device ip or local file path.
28
29 Returns a log object containg activity, evdev and system logs.
30 source can be either an ip address to a device from which to
31 download, a url pointing to a feedback report to pull or a filename.
Dennis Kempin351024d2013-02-06 11:18:21 -080032 """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070033 if source:
34 if os.path.exists(source):
35 if source.endswith('.zip') or source.endswith('.bz2'):
36 return FeedbackLog(source, options)
37 return FileLog(source, options)
Dennis Kempin351024d2013-02-06 11:18:21 -080038 else:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070039 match = re.search('Report/([0-9]+)', source)
40 if match:
41 return FeedbackLog(match.group(1), options)
42 else:
43 # todo add regex and error message
44 return DeviceLog(source, options)
45
46 if activity or evdev:
47 log = AbstractLog()
48 if activity:
49 if os.path.exists(activity):
50 log.activity = open(activity).read()
51 else:
52 log.activity = activity
53 if evdev:
54 if os.path.exists(evdev):
55 log.evdev = open(evdev).read()
56 else:
57 log.evdev = evdev
58 return log
Dennis Kempin351024d2013-02-06 11:18:21 -080059
60
61class AbstractLog(object):
Dennis Kempin17766a62013-06-17 14:09:33 -070062 """ Common functions for log files
63
64 A log file consists of the activity log, the evdev log, a system log, and
65 possibly a screenshot, which can be saved to disk all together.
Dennis Kempin351024d2013-02-06 11:18:21 -080066 """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070067 def __init__(self):
68 self.activity = None
69 self.evdev = None
70 self.system = None
71 self.image = None
72
Dennis Kempin351024d2013-02-06 11:18:21 -080073 def SaveAs(self, filename):
Dennis Kempin17766a62013-06-17 14:09:33 -070074 open(filename, 'w').write(self.activity)
Dennis Kempin351024d2013-02-06 11:18:21 -080075 if self.evdev:
Dennis Kempin17766a62013-06-17 14:09:33 -070076 open(filename + '.evdev', 'w').write(self.evdev)
Dennis Kempin351024d2013-02-06 11:18:21 -080077 if self.system:
Dennis Kempin17766a62013-06-17 14:09:33 -070078 open(filename + '.system', 'w').write(self.system)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040079 if self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -070080 open(filename + '.jpg', 'w').write(self.image)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040081
82 def CleanUp(self):
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040083 if os.path.exists(screenshot_filepath):
84 os.remove(screenshot_filepath)
Dennis Kempin351024d2013-02-06 11:18:21 -080085
86
87class FileLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -070088 """ Loads log from file.
89
90 evdev or system logs are optional.
Dennis Kempin351024d2013-02-06 11:18:21 -080091 """
92 def __init__(self, filename, options):
93 self.activity = open(filename).read()
Dennis Kempin17766a62013-06-17 14:09:33 -070094 self.evdev = ''
95 self.system = ''
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040096 self.image = None
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070097 if options and options.evdev:
Dennis Kempin7b783272013-04-01 15:06:35 -070098 self.evdev = open(options.evdev).read()
Dennis Kempin17766a62013-06-17 14:09:33 -070099 elif os.path.exists(filename + '.evdev'):
100 self.evdev = open(filename + '.evdev').read()
101 if os.path.exists(filename + '.system'):
102 self.system = open(filename + '.system').read()
103 if os.path.exists(filename + '.jpg'):
104 self.image = open(filename + '.jpg').read()
105 file(screenshot_filepath, 'w').write(self.image)
Dennis Kempin351024d2013-02-06 11:18:21 -0800106
107
108class FeedbackLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700109 """ Download log from feedback url.
110
111 Downloads logs and (possibly) screenshot from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -0800112 """
Dennis Kempinb049d542013-06-13 13:55:18 -0700113 def __init__(self, id_or_filename, options=None, force_latest=None):
WeiNan-Peter, Wene539a182013-05-16 15:31:21 -0400114 self.image = None
Dennis Kempinb049d542013-06-13 13:55:18 -0700115 self.force_latest = force_latest
116 self.try_screenshot = options and options.screenshot
Dennis Kempin19e972b2013-06-20 13:21:38 -0700117 self.report = None
118 self.system = None
119 self.activity = None
120 self.evdev = None
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400121
Dennis Kempin17766a62013-06-17 14:09:33 -0700122 if id_or_filename.endswith('.zip') or id_or_filename.endswith('.bz2'):
Dennis Kempin91e96df2013-03-29 14:21:42 -0700123 self.report = open(id_or_filename).read()
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400124 screenshot_filename = id_or_filename[:-4] + '.jpg'
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700125 try:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400126 self.image = open(screenshot_filename).read()
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700127 except IOError:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400128 # No corresponding screenshot available.
129 pass
130 else:
Dennis Kempinb049d542013-06-13 13:55:18 -0700131 self.downloader = FeedbackDownloader()
Dennis Kempincee37612013-06-14 10:40:41 -0700132 self.report = self.downloader.DownloadSystemLog(id_or_filename)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400133 if self.try_screenshot:
Dennis Kempincee37612013-06-14 10:40:41 -0700134 self.image = self.downloader.DownloadScreenshot(id_or_filename)
Dennis Kempin351024d2013-02-06 11:18:21 -0800135
Dennis Kempin13d948e2014-04-18 11:23:32 -0700136 if self.report:
137 self._ExtractSystemLog()
138 if self.system:
139 self._ExtractLogFiles()
140
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400141 # Only write to screenshot.jpg if we will be viewing the screenshot
Dennis Kempinb049d542013-06-13 13:55:18 -0700142 if options and not options.download and self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -0700143 file(screenshot_filepath, 'w').write(self.image)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400144
Dennis Kempin351024d2013-02-06 11:18:21 -0800145
146 def _GetLatestFile(self, match, tar):
147 # find file name containing match with latest timestamp
148 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
149 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700150 if not names:
Dennis Kempin17766a62013-06-17 14:09:33 -0700151 print 'Cannot find files named %s in tar file' % match
152 print 'Tar file contents:', tar.getnames()
Dennis Kempin765ad942013-03-26 13:58:46 -0700153 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800154 return tar.extractfile(names[-1])
155
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -0400156
Dennis Kempin91e96df2013-03-29 14:21:42 -0700157 def _ExtractSystemLog(self):
Dennis Kempin17766a62013-06-17 14:09:33 -0700158 if self.report[0:2] == 'BZ':
Dennis Kempin91e96df2013-03-29 14:21:42 -0700159 self.system = bz2.decompress(self.report)
Dennis Kempin17766a62013-06-17 14:09:33 -0700160 elif self.report[0:2] == 'PK':
Dennis Kempin91e96df2013-03-29 14:21:42 -0700161 io = StringIO(self.report)
Dennis Kempin17766a62013-06-17 14:09:33 -0700162 zip = zipfile.ZipFile(io, 'r')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700163 self.system = zip.read(zip.namelist()[0])
Dennis Kempin17766a62013-06-17 14:09:33 -0700164 zip.extractall('./')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700165 else:
Dennis Kempin17766a62013-06-17 14:09:33 -0700166 print 'Cannot download logs file'
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700167 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800168
169 def _ExtractLogFiles(self):
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400170 # Find embedded and uuencoded activity.tar in system log
171
172 def ExtractByInterface(interface):
173 assert interface == 'pad' or interface == 'screen'
174
175 log_index = [
Dennis Kempin19e972b2013-06-20 13:21:38 -0700176 (('hack-33025-touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700177 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
178 '"""'),
Dennis Kempin19e972b2013-06-20 13:21:38 -0700179 (('touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700180 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
181 '"""'),
182 (('hack-33025-touch{0}_activity=<multiline>\n' +
183 '---------- START ----------\n' +
184 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
185 '---------- END ----------'),
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400186 ]
187
188 start_index = end_index = None
189 for start, end in log_index:
190 if start in self.system:
191 start_index = self.system.index(start) + len(start)
192 end_index = self.system.index(end, start_index)
193 break
194
195 if start_index is None:
196 return []
197
198 activity_tar_enc = self.system[start_index:end_index]
199
200 # base64 decode
201 activity_tar_data = base64.b64decode(activity_tar_enc)
202
203 # untar
204 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
205
206 def ExtractPadFiles(name):
207 # find matching evdev file
208 evdev_name = name[0:name.index('touchpad_activity')]
Dennis Kempin17766a62013-06-17 14:09:33 -0700209 evdev_name = evdev_name + 'cmt_input_events'
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400210
211 for file_name in activity_tar_file.getnames():
212 if file_name.startswith(evdev_name):
213 evdev_name = file_name
214 break
215
216 activity_gz = activity_tar_file.extractfile(name)
217 evdev_gz = activity_tar_file.extractfile(evdev_name)
218
219 # decompress log files
220 return (GzipFile(fileobj=activity_gz).read(),
221 GzipFile(fileobj=evdev_gz).read())
222
223 def ExtractScreenFiles(name):
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400224 # Always try for a screenshot with touchscreen view
225 self.try_screenshot = True
226
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400227 evdev = activity_tar_file.extractfile(name)
228 return (evdev.read(), None)
229
230 extract_func = {
231 'pad': ExtractPadFiles,
232 'screen': ExtractScreenFiles,
233 }
234
235 # return latest log files, we don't include cmt files
236 return [(filename, extract_func[interface])
237 for filename in activity_tar_file.getnames()
238 if filename.find('cmt') == -1]
239
Dennis Kempin17766a62013-06-17 14:09:33 -0700240 if self.force_latest == 'pad':
Dennis Kempinb049d542013-06-13 13:55:18 -0700241 logs = ExtractByInterface('pad')
242 idx = 0
Dennis Kempin17766a62013-06-17 14:09:33 -0700243 elif self.force_latest == 'screen':
Dennis Kempinb049d542013-06-13 13:55:18 -0700244 logs = ExtractByInterface('screen')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700245 idx = 0
246 else:
Dennis Kempinb049d542013-06-13 13:55:18 -0700247 logs = ExtractByInterface('pad') + ExtractByInterface('screen')
248 if not logs:
Dennis Kempin17766a62013-06-17 14:09:33 -0700249 print ('Cannot find touchpad_activity_log.tar or ' +
250 'touchscreen_activity_log.tar in systems log file.')
Dennis Kempinb049d542013-06-13 13:55:18 -0700251 sys.exit(-1)
252
253 if len(logs) == 1:
254 idx = 0
255 else:
256 while True:
Dennis Kempin17766a62013-06-17 14:09:33 -0700257 print 'Which log file would you like to use?'
Dennis Kempinb049d542013-06-13 13:55:18 -0700258 for i, (name, extract_func) in enumerate(logs):
259 if name.startswith('evdev'):
260 name = 'touchscreen log - ' + name
261 print i, name
Dennis Kempin17766a62013-06-17 14:09:33 -0700262 print '>'
Dennis Kempinb049d542013-06-13 13:55:18 -0700263 selection = sys.stdin.readline()
264 try:
265 idx = int(selection)
266 if idx < 0 or idx >= len(logs):
Dennis Kempin17766a62013-06-17 14:09:33 -0700267 print 'Number out of range'
Dennis Kempinb049d542013-06-13 13:55:18 -0700268 else:
269 break
270 except:
Dennis Kempin17766a62013-06-17 14:09:33 -0700271 print 'Not a number'
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700272
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400273 name, extract_func = logs[idx]
274 self.activity, self.evdev = extract_func(name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800275
276
277identity_message = """\
278In order to access devices in TPTool, you need to have password-less
279auth for chromebooks set up.
280Would you like tptool to run the following command for you?
281$ %s
282Yes/No? (Default: No)"""
283
284class DeviceLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700285 """ Downloads logs from a running chromebook via scp. """
Dennis Kempind0b722a2014-04-15 11:54:48 -0700286 def __init__(self, ip, options=None, new=False):
Dennis Kempin60571e02014-01-09 12:00:15 -0800287 self.remote = CrOSRemote(ip)
Dennis Kempin351024d2013-02-06 11:18:21 -0800288
Dennis Kempind0b722a2014-04-15 11:54:48 -0700289 if new or (options and options.new):
Dennis Kempin60571e02014-01-09 12:00:15 -0800290 self.remote.Execute('/opt/google/touchpad/tpcontrol log')
Dennis Kempin351024d2013-02-06 11:18:21 -0800291
Dennis Kempin60571e02014-01-09 12:00:15 -0800292 self.activity = self.remote.Read('/var/log/xorg/touchpad_activity_log.txt')
293 self.evdev = self.remote.Read('/var/log/xorg/cmt_input_events.dat')
Dennis Kempin17766a62013-06-17 14:09:33 -0700294 self.system = ''
Dennis Kempin13d948e2014-04-18 11:23:32 -0700295 self.image = None