blob: e3981017395284addc34570614ced947df9f7c55 [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
Harry Cutts69fc2be2020-01-22 18:03:21 -08005from __future__ import absolute_import
6from __future__ import division
Harry Cutts0edf1572020-01-21 15:42:10 -08007from __future__ import print_function
8
Dennis Kempin351024d2013-02-06 11:18:21 -08009from gzip import GzipFile
Dennis Kempincee37612013-06-14 10:40:41 -070010from mtlib.feedback import FeedbackDownloader
Harry Cuttsd77ab7a2020-01-28 11:09:26 -080011from io import BytesIO
Harry Cutts69fc2be2020-01-22 18:03:21 -080012from .cros_remote import CrOSRemote
Dennis Kempin351024d2013-02-06 11:18:21 -080013import base64
14import bz2
Dennis Kempin351024d2013-02-06 11:18:21 -080015import os
16import os.path
17import re
Dennis Kempin351024d2013-02-06 11:18:21 -080018import sys
19import tarfile
Dennis Kempinecd9a0c2013-03-27 16:03:46 -070020import zipfile
Dennis Kempin351024d2013-02-06 11:18:21 -080021
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040022# path for temporary screenshot
Dennis Kempin17766a62013-06-17 14:09:33 -070023screenshot_filepath = 'extension/screenshot.jpg'
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040024
Dennis Kempinaa7ce272014-04-18 11:50:59 -070025class Options:
26 def __init__(self, download=False, new=False, screenshot=False,
27 evdev=None):
28 self.download = download
29 self.new = new
30 self.screenshot = screenshot
31 self.evdev = evdev
32
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070033def Log(source=None, options=None, activity=None, evdev=None):
Dennis Kempin17766a62013-06-17 14:09:33 -070034 """ Load log from feedback url, device ip or local file path.
35
36 Returns a log object containg activity, evdev and system logs.
37 source can be either an ip address to a device from which to
38 download, a url pointing to a feedback report to pull or a filename.
Dennis Kempin351024d2013-02-06 11:18:21 -080039 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -070040 if not options:
41 options = Options()
42
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070043 if source:
44 if os.path.exists(source):
45 if source.endswith('.zip') or source.endswith('.bz2'):
Dennis Kempinaa7ce272014-04-18 11:50:59 -070046 return FeedbackLog(source)
47 return FileLog(source, options.evdev)
Dennis Kempin351024d2013-02-06 11:18:21 -080048 else:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070049 match = re.search('Report/([0-9]+)', source)
50 if match:
Dennis Kempinaa7ce272014-04-18 11:50:59 -070051 return FeedbackLog(match.group(1), options.screenshot,
52 options.download)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070053 else:
54 # todo add regex and error message
Dennis Kempinaa7ce272014-04-18 11:50:59 -070055 return DeviceLog(source, options.new)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070056
57 if activity or evdev:
58 log = AbstractLog()
59 if activity:
60 if os.path.exists(activity):
61 log.activity = open(activity).read()
62 else:
63 log.activity = activity
64 if evdev:
65 if os.path.exists(evdev):
66 log.evdev = open(evdev).read()
67 else:
68 log.evdev = evdev
69 return log
Dennis Kempin351024d2013-02-06 11:18:21 -080070
71
72class AbstractLog(object):
Dennis Kempin17766a62013-06-17 14:09:33 -070073 """ Common functions for log files
74
75 A log file consists of the activity log, the evdev log, a system log, and
76 possibly a screenshot, which can be saved to disk all together.
Dennis Kempin351024d2013-02-06 11:18:21 -080077 """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070078 def __init__(self):
Dennis Kempinaa7ce272014-04-18 11:50:59 -070079 self.activity = ''
80 self.evdev = ''
81 self.system = ''
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070082 self.image = None
83
Dennis Kempin351024d2013-02-06 11:18:21 -080084 def SaveAs(self, filename):
Dennis Kempin17766a62013-06-17 14:09:33 -070085 open(filename, 'w').write(self.activity)
Dennis Kempin351024d2013-02-06 11:18:21 -080086 if self.evdev:
Dennis Kempin17766a62013-06-17 14:09:33 -070087 open(filename + '.evdev', 'w').write(self.evdev)
Dennis Kempin351024d2013-02-06 11:18:21 -080088 if self.system:
Harry Cutts85378ee2020-02-07 15:53:46 -080089 open(filename + '.system', 'wb').write(
90 self.system.encode('utf-8', 'strict'))
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040091 if self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -070092 open(filename + '.jpg', 'w').write(self.image)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040093
94 def CleanUp(self):
WeiNan-Peter, Wen294408a2013-06-13 10:48:39 -040095 if os.path.exists(screenshot_filepath):
96 os.remove(screenshot_filepath)
Dennis Kempin351024d2013-02-06 11:18:21 -080097
98
99class FileLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700100 """ Loads log from file.
101
102 evdev or system logs are optional.
Dennis Kempin351024d2013-02-06 11:18:21 -0800103 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700104 def __init__(self, filename, evdev=None):
105 AbstractLog.__init__(self)
106
Dennis Kempin351024d2013-02-06 11:18:21 -0800107 self.activity = open(filename).read()
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700108 if evdev:
109 self.evdev = open(evdev).read()
Dennis Kempin17766a62013-06-17 14:09:33 -0700110 elif os.path.exists(filename + '.evdev'):
111 self.evdev = open(filename + '.evdev').read()
112 if os.path.exists(filename + '.system'):
113 self.system = open(filename + '.system').read()
114 if os.path.exists(filename + '.jpg'):
115 self.image = open(filename + '.jpg').read()
116 file(screenshot_filepath, 'w').write(self.image)
Dennis Kempin351024d2013-02-06 11:18:21 -0800117
118
119class FeedbackLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700120 """ Download log from feedback url.
121
122 Downloads logs and (possibly) screenshot from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -0800123 """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700124 def __init__(self, id_or_filename, screenshot=False, download=False,
Sean O'Brienfd204da2017-05-02 15:13:11 -0700125 force_latest=None, downloader=None):
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700126 AbstractLog.__init__(self)
Dennis Kempinb049d542013-06-13 13:55:18 -0700127 self.force_latest = force_latest
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700128 self.try_screenshot = screenshot
Dennis Kempin19e972b2013-06-20 13:21:38 -0700129 self.report = None
Sean O'Brienfd204da2017-05-02 15:13:11 -0700130 self.downloader = downloader
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400131
Dennis Kempin17766a62013-06-17 14:09:33 -0700132 if id_or_filename.endswith('.zip') or id_or_filename.endswith('.bz2'):
Harry Cuttsd77ab7a2020-01-28 11:09:26 -0800133 self.report = open(id_or_filename, 'rb').read()
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400134 screenshot_filename = id_or_filename[:-4] + '.jpg'
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700135 try:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400136 self.image = open(screenshot_filename).read()
Andrew de los Reyes1de4dcd2013-05-14 11:45:32 -0700137 except IOError:
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400138 # No corresponding screenshot available.
139 pass
140 else:
Sean O'Brienfd204da2017-05-02 15:13:11 -0700141 if not self.downloader:
142 self.downloader = FeedbackDownloader()
Dennis Kempincee37612013-06-14 10:40:41 -0700143 self.report = self.downloader.DownloadSystemLog(id_or_filename)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400144 if self.try_screenshot:
Dennis Kempincee37612013-06-14 10:40:41 -0700145 self.image = self.downloader.DownloadScreenshot(id_or_filename)
Dennis Kempin351024d2013-02-06 11:18:21 -0800146
Dennis Kempin13d948e2014-04-18 11:23:32 -0700147 if self.report:
148 self._ExtractSystemLog()
149 if self.system:
150 self._ExtractLogFiles()
151
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400152 # Only write to screenshot.jpg if we will be viewing the screenshot
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700153 if not download and self.image:
Dennis Kempin17766a62013-06-17 14:09:33 -0700154 file(screenshot_filepath, 'w').write(self.image)
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400155
Dennis Kempin351024d2013-02-06 11:18:21 -0800156 def _GetLatestFile(self, match, tar):
157 # find file name containing match with latest timestamp
158 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
159 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700160 if not names:
Harry Cutts0edf1572020-01-21 15:42:10 -0800161 print('Cannot find files named %s in tar file' % match)
162 print('Tar file contents:', tar.getnames())
Dennis Kempin765ad942013-03-26 13:58:46 -0700163 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800164 return tar.extractfile(names[-1])
165
Dennis Kempin91e96df2013-03-29 14:21:42 -0700166 def _ExtractSystemLog(self):
Harry Cuttsd77ab7a2020-01-28 11:09:26 -0800167 if self.report[0:2] == b'BZ':
168 system_log = bz2.decompress(self.report)
169 elif self.report[0:2] == b'PK':
170 bytes_io = BytesIO(self.report)
171 zip = zipfile.ZipFile(bytes_io, 'r')
172 system_log = zip.read(zip.namelist()[0])
Dennis Kempin17766a62013-06-17 14:09:33 -0700173 zip.extractall('./')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700174 else:
Harry Cutts0edf1572020-01-21 15:42:10 -0800175 print('Cannot download logs file')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700176 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800177
Harry Cuttsd77ab7a2020-01-28 11:09:26 -0800178 self.system = system_log.decode('utf-8', 'strict')
179
Dennis Kempin351024d2013-02-06 11:18:21 -0800180 def _ExtractLogFiles(self):
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400181 # Find embedded and uuencoded activity.tar in system log
182
183 def ExtractByInterface(interface):
184 assert interface == 'pad' or interface == 'screen'
185
186 log_index = [
Dennis Kempin19e972b2013-06-20 13:21:38 -0700187 (('hack-33025-touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700188 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
189 '"""'),
Dennis Kempin19e972b2013-06-20 13:21:38 -0700190 (('touch{0}_activity=\"\"\"\n' +
Dennis Kempin17766a62013-06-17 14:09:33 -0700191 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
192 '"""'),
193 (('hack-33025-touch{0}_activity=<multiline>\n' +
194 '---------- START ----------\n' +
195 'begin-base64 644 touch{0}_activity_log.tar\n').format(interface),
196 '---------- END ----------'),
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400197 ]
198
199 start_index = end_index = None
200 for start, end in log_index:
201 if start in self.system:
202 start_index = self.system.index(start) + len(start)
203 end_index = self.system.index(end, start_index)
204 break
205
206 if start_index is None:
207 return []
208
209 activity_tar_enc = self.system[start_index:end_index]
210
211 # base64 decode
212 activity_tar_data = base64.b64decode(activity_tar_enc)
213
Sean O'Brien0d2d0c42018-02-06 09:37:42 -0800214 if activity_tar_data == '':
Harry Cutts0edf1572020-01-21 15:42:10 -0800215 print('touch{0}_activity_log.tar found, but empty'.format(interface))
Sean O'Brien0d2d0c42018-02-06 09:37:42 -0800216 return []
217
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400218 # untar
Harry Cuttsd77ab7a2020-01-28 11:09:26 -0800219 activity_tar_file = tarfile.open(fileobj=BytesIO(activity_tar_data))
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400220
221 def ExtractPadFiles(name):
222 # find matching evdev file
Sean O'Brien146a6842018-07-09 09:09:40 -0700223 evdev_name = name.replace('touchpad_activity', 'cmt_input_events')
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400224
225 activity_gz = activity_tar_file.extractfile(name)
226 evdev_gz = activity_tar_file.extractfile(evdev_name)
227
228 # decompress log files
229 return (GzipFile(fileobj=activity_gz).read(),
230 GzipFile(fileobj=evdev_gz).read())
231
232 def ExtractScreenFiles(name):
WeiNan-Peter, Wen46258862013-05-21 11:17:54 -0400233 # Always try for a screenshot with touchscreen view
234 self.try_screenshot = True
235
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400236 evdev = activity_tar_file.extractfile(name)
237 return (evdev.read(), None)
238
239 extract_func = {
240 'pad': ExtractPadFiles,
241 'screen': ExtractScreenFiles,
242 }
243
244 # return latest log files, we don't include cmt files
245 return [(filename, extract_func[interface])
246 for filename in activity_tar_file.getnames()
247 if filename.find('cmt') == -1]
248
Dennis Kempin17766a62013-06-17 14:09:33 -0700249 if self.force_latest == 'pad':
Dennis Kempinb049d542013-06-13 13:55:18 -0700250 logs = ExtractByInterface('pad')
251 idx = 0
Dennis Kempin17766a62013-06-17 14:09:33 -0700252 elif self.force_latest == 'screen':
Dennis Kempinb049d542013-06-13 13:55:18 -0700253 logs = ExtractByInterface('screen')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700254 idx = 0
255 else:
Dennis Kempinb049d542013-06-13 13:55:18 -0700256 logs = ExtractByInterface('pad') + ExtractByInterface('screen')
257 if not logs:
Harry Cutts0edf1572020-01-21 15:42:10 -0800258 print('Cannot find touchpad_activity_log.tar or '
Dennis Kempin17766a62013-06-17 14:09:33 -0700259 'touchscreen_activity_log.tar in systems log file.')
Dennis Kempinb049d542013-06-13 13:55:18 -0700260 sys.exit(-1)
261
262 if len(logs) == 1:
263 idx = 0
264 else:
265 while True:
Harry Cutts0edf1572020-01-21 15:42:10 -0800266 print('Which log file would you like to use?')
Dennis Kempinb049d542013-06-13 13:55:18 -0700267 for i, (name, extract_func) in enumerate(logs):
268 if name.startswith('evdev'):
269 name = 'touchscreen log - ' + name
Harry Cutts0edf1572020-01-21 15:42:10 -0800270 print(i, name)
271 print('>')
Dennis Kempinb049d542013-06-13 13:55:18 -0700272 selection = sys.stdin.readline()
273 try:
274 idx = int(selection)
275 if idx < 0 or idx >= len(logs):
Harry Cutts0edf1572020-01-21 15:42:10 -0800276 print('Number out of range')
Dennis Kempinb049d542013-06-13 13:55:18 -0700277 else:
278 break
279 except:
Harry Cutts0edf1572020-01-21 15:42:10 -0800280 print('Not a number')
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700281
WeiNan-Peter, Wenf3f40342013-05-15 11:26:09 -0400282 name, extract_func = logs[idx]
Harry Cutts87cc6002020-02-04 15:50:20 -0800283 activity, evdev = extract_func(name)
284 self.activity = activity.decode('utf-8', 'strict')
285 self.evdev = evdev.decode('utf-8', 'strict')
Dennis Kempin351024d2013-02-06 11:18:21 -0800286
287
288identity_message = """\
289In order to access devices in TPTool, you need to have password-less
290auth for chromebooks set up.
291Would you like tptool to run the following command for you?
292$ %s
293Yes/No? (Default: No)"""
294
295class DeviceLog(AbstractLog):
Dennis Kempin17766a62013-06-17 14:09:33 -0700296 """ Downloads logs from a running chromebook via scp. """
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700297 def __init__(self, ip, new=False):
298 AbstractLog.__init__(self)
Dennis Kempin60571e02014-01-09 12:00:15 -0800299 self.remote = CrOSRemote(ip)
Dennis Kempin351024d2013-02-06 11:18:21 -0800300
Dennis Kempinaa7ce272014-04-18 11:50:59 -0700301 if new:
Dennis Kempin58d9a742014-05-08 18:34:59 -0700302 self.remote.SafeExecute('/opt/google/input/inputcontrol -t touchpad' +
303 ' --log')
Dennis Kempin60571e02014-01-09 12:00:15 -0800304 self.activity = self.remote.Read('/var/log/xorg/touchpad_activity_log.txt')
305 self.evdev = self.remote.Read('/var/log/xorg/cmt_input_events.dat')