xref: /aosp_15_r20/external/libchrome-gestures/tools/tplog.py (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
1*aed3e508SAndroid Build Coastguard Worker#!/usr/bin/python
2*aed3e508SAndroid Build Coastguard Worker#
3*aed3e508SAndroid Build Coastguard Worker# Copyright 2012 The ChromiumOS Authors
4*aed3e508SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*aed3e508SAndroid Build Coastguard Worker# found in the LICENSE file.
6*aed3e508SAndroid Build Coastguard Worker
7*aed3e508SAndroid Build Coastguard Worker"""TPLog Manipulation"""
8*aed3e508SAndroid Build Coastguard Worker
9*aed3e508SAndroid Build Coastguard Worker
10*aed3e508SAndroid Build Coastguard Workerimport getopt
11*aed3e508SAndroid Build Coastguard Workerimport json
12*aed3e508SAndroid Build Coastguard Workerimport logging
13*aed3e508SAndroid Build Coastguard Workerimport os
14*aed3e508SAndroid Build Coastguard Workerimport sys
15*aed3e508SAndroid Build Coastguard Worker
16*aed3e508SAndroid Build Coastguard Workerfrom operator import ge, lt
17*aed3e508SAndroid Build Coastguard Worker
18*aed3e508SAndroid Build Coastguard Worker
19*aed3e508SAndroid Build Coastguard Workerclass TPLog:
20*aed3e508SAndroid Build Coastguard Worker  """TPLog Manipulation"""
21*aed3e508SAndroid Build Coastguard Worker  # Constants for entry type
22*aed3e508SAndroid Build Coastguard Worker  CALLBACK_REQUEST = 'callbackRequest'
23*aed3e508SAndroid Build Coastguard Worker  GESTURE = 'gesture'
24*aed3e508SAndroid Build Coastguard Worker  HARDWARE_STATE = 'hardwareState'
25*aed3e508SAndroid Build Coastguard Worker  PROPERTY_CHANGE = 'propertyChange'
26*aed3e508SAndroid Build Coastguard Worker  TIMER_CALLBACK = 'timerCallback'
27*aed3e508SAndroid Build Coastguard Worker
28*aed3e508SAndroid Build Coastguard Worker  # Constants for log keys
29*aed3e508SAndroid Build Coastguard Worker  DESCRIPTION = 'description'
30*aed3e508SAndroid Build Coastguard Worker  ENTRIES = 'entries'
31*aed3e508SAndroid Build Coastguard Worker  GESTURES_VERSION = 'gesturesVersion'
32*aed3e508SAndroid Build Coastguard Worker  HARDWARE_PROPERTIES = 'hardwareProperties'
33*aed3e508SAndroid Build Coastguard Worker  PROPERTIES = 'properties'
34*aed3e508SAndroid Build Coastguard Worker  VERSION = 'version'
35*aed3e508SAndroid Build Coastguard Worker
36*aed3e508SAndroid Build Coastguard Worker  # Constants for entry keys
37*aed3e508SAndroid Build Coastguard Worker  END_TIME = 'endTime'
38*aed3e508SAndroid Build Coastguard Worker  START_TIME = 'startTime'
39*aed3e508SAndroid Build Coastguard Worker  TIMESTAMP = 'timestamp'
40*aed3e508SAndroid Build Coastguard Worker  TYPE = 'type'
41*aed3e508SAndroid Build Coastguard Worker
42*aed3e508SAndroid Build Coastguard Worker  def __init__(self, log_file):
43*aed3e508SAndroid Build Coastguard Worker    self._load_log(log_file)
44*aed3e508SAndroid Build Coastguard Worker    self._setup_get_time_functions()
45*aed3e508SAndroid Build Coastguard Worker
46*aed3e508SAndroid Build Coastguard Worker  def _load_log(self, log_file):
47*aed3e508SAndroid Build Coastguard Worker    """Load the json file."""
48*aed3e508SAndroid Build Coastguard Worker    with open(log_file) as f:
49*aed3e508SAndroid Build Coastguard Worker      self.log = json.load(f)
50*aed3e508SAndroid Build Coastguard Worker    self.shrunk_log = {}
51*aed3e508SAndroid Build Coastguard Worker    # Build a new shrunk log from the original log so that we could
52*aed3e508SAndroid Build Coastguard Worker    # modify the entries and properties, and add description later.
53*aed3e508SAndroid Build Coastguard Worker    self.shrunk_log = self.log.copy()
54*aed3e508SAndroid Build Coastguard Worker    self.entries = self.log[self.ENTRIES]
55*aed3e508SAndroid Build Coastguard Worker
56*aed3e508SAndroid Build Coastguard Worker  def _setup_get_time_functions(self):
57*aed3e508SAndroid Build Coastguard Worker    """Set up get time functions for hardware state, gesture,
58*aed3e508SAndroid Build Coastguard Worker    and timer callback."""
59*aed3e508SAndroid Build Coastguard Worker    self._get_time = {self.HARDWARE_STATE: self._get_hwstate_time,
60*aed3e508SAndroid Build Coastguard Worker                      self.GESTURE: self._get_gesture_end_time,
61*aed3e508SAndroid Build Coastguard Worker                      self.TIMER_CALLBACK: self._get_timercb_time}
62*aed3e508SAndroid Build Coastguard Worker
63*aed3e508SAndroid Build Coastguard Worker  def _get_hwstate_time(self, entry):
64*aed3e508SAndroid Build Coastguard Worker    """Get the timestamp of a hardware state entry."""
65*aed3e508SAndroid Build Coastguard Worker    if entry[self.TYPE] == self.HARDWARE_STATE:
66*aed3e508SAndroid Build Coastguard Worker      return entry[self.TIMESTAMP]
67*aed3e508SAndroid Build Coastguard Worker    else:
68*aed3e508SAndroid Build Coastguard Worker      return None
69*aed3e508SAndroid Build Coastguard Worker
70*aed3e508SAndroid Build Coastguard Worker  def _get_gesture_end_time(self, entry):
71*aed3e508SAndroid Build Coastguard Worker    """Get the end timestamp of a gesture entry."""
72*aed3e508SAndroid Build Coastguard Worker    if entry[self.TYPE] == self.GESTURE:
73*aed3e508SAndroid Build Coastguard Worker      return entry[self.END_TIME]
74*aed3e508SAndroid Build Coastguard Worker    else:
75*aed3e508SAndroid Build Coastguard Worker      return None
76*aed3e508SAndroid Build Coastguard Worker
77*aed3e508SAndroid Build Coastguard Worker  def _get_timercb_time(self, entry):
78*aed3e508SAndroid Build Coastguard Worker    """Get the timestamp of a timer callback entry."""
79*aed3e508SAndroid Build Coastguard Worker    if entry[self.TYPE] == self.TIMER_CALLBACK:
80*aed3e508SAndroid Build Coastguard Worker      return entry['now']
81*aed3e508SAndroid Build Coastguard Worker    else:
82*aed3e508SAndroid Build Coastguard Worker      return None
83*aed3e508SAndroid Build Coastguard Worker
84*aed3e508SAndroid Build Coastguard Worker  def _get_entry_time(self, entry):
85*aed3e508SAndroid Build Coastguard Worker    """Get the timestamp of the given entry."""
86*aed3e508SAndroid Build Coastguard Worker    e_type = entry[self.TYPE]
87*aed3e508SAndroid Build Coastguard Worker    if self._get_time.get(e_type):
88*aed3e508SAndroid Build Coastguard Worker      return self._get_time[e_type](entry)
89*aed3e508SAndroid Build Coastguard Worker    return None
90*aed3e508SAndroid Build Coastguard Worker
91*aed3e508SAndroid Build Coastguard Worker  def _compare_entry_time(self, entry, timestamp, op):
92*aed3e508SAndroid Build Coastguard Worker    """Compare entry time with a given timestamp using the operator op."""
93*aed3e508SAndroid Build Coastguard Worker    e_time = self._get_entry_time(entry)
94*aed3e508SAndroid Build Coastguard Worker    return e_time and op(e_time, timestamp)
95*aed3e508SAndroid Build Coastguard Worker
96*aed3e508SAndroid Build Coastguard Worker  def _get_begin_hwstate(self, timestamp):
97*aed3e508SAndroid Build Coastguard Worker    """Get the hardwareState entry after the specified timestamp."""
98*aed3e508SAndroid Build Coastguard Worker    for index, e in enumerate(self.entries):
99*aed3e508SAndroid Build Coastguard Worker      if (e[self.TYPE] == self.HARDWARE_STATE and
100*aed3e508SAndroid Build Coastguard Worker          self._get_hwstate_time(e) >= timestamp):
101*aed3e508SAndroid Build Coastguard Worker        return index
102*aed3e508SAndroid Build Coastguard Worker    return None
103*aed3e508SAndroid Build Coastguard Worker
104*aed3e508SAndroid Build Coastguard Worker  def _get_end_entry(self, timestamp):
105*aed3e508SAndroid Build Coastguard Worker    """Get the entry after the specified timestamp."""
106*aed3e508SAndroid Build Coastguard Worker    for index, e in enumerate(self.entries):
107*aed3e508SAndroid Build Coastguard Worker      if self._compare_entry_time(e, timestamp, ge):
108*aed3e508SAndroid Build Coastguard Worker        return index
109*aed3e508SAndroid Build Coastguard Worker    return None
110*aed3e508SAndroid Build Coastguard Worker
111*aed3e508SAndroid Build Coastguard Worker  def _get_end_gesture(self, timestamp):
112*aed3e508SAndroid Build Coastguard Worker    """Get the gesture entry after the specified timestamp."""
113*aed3e508SAndroid Build Coastguard Worker    end_entry = None
114*aed3e508SAndroid Build Coastguard Worker    entry_len = len(self.entries)
115*aed3e508SAndroid Build Coastguard Worker    for index, e in enumerate(reversed(self.entries)):
116*aed3e508SAndroid Build Coastguard Worker      # Try to find the last gesture entry resulted from the events within the
117*aed3e508SAndroid Build Coastguard Worker      # timestamp.
118*aed3e508SAndroid Build Coastguard Worker      if (e[self.TYPE] == self.GESTURE and
119*aed3e508SAndroid Build Coastguard Worker          self._get_gesture_end_time(e) <= timestamp):
120*aed3e508SAndroid Build Coastguard Worker        return entry_len - index - 1
121*aed3e508SAndroid Build Coastguard Worker      # Keep the entry with timestamp >= the specified timestamp
122*aed3e508SAndroid Build Coastguard Worker      elif self._compare_entry_time(e, timestamp, ge):
123*aed3e508SAndroid Build Coastguard Worker        end_entry = entry_len - index - 1
124*aed3e508SAndroid Build Coastguard Worker      elif self._compare_entry_time(e, timestamp, lt):
125*aed3e508SAndroid Build Coastguard Worker        return end_entry
126*aed3e508SAndroid Build Coastguard Worker    return end_entry
127*aed3e508SAndroid Build Coastguard Worker
128*aed3e508SAndroid Build Coastguard Worker  def shrink(self, bgn_time=None, end_time=None, end_gesture_flag=True):
129*aed3e508SAndroid Build Coastguard Worker    """Shrink the log according to the begin time and end time.
130*aed3e508SAndroid Build Coastguard Worker
131*aed3e508SAndroid Build Coastguard Worker    end_gesture_flag:
132*aed3e508SAndroid Build Coastguard Worker      When set to True, the shrunk log will contain the gestures resulted from
133*aed3e508SAndroid Build Coastguard Worker      the activities within the time range.
134*aed3e508SAndroid Build Coastguard Worker      When set to False, the shrunk log will have a hard cut at the entry
135*aed3e508SAndroid Build Coastguard Worker      with the smallest timestamp greater than or equal to the specified
136*aed3e508SAndroid Build Coastguard Worker      end_time.
137*aed3e508SAndroid Build Coastguard Worker    """
138*aed3e508SAndroid Build Coastguard Worker    if bgn_time is not None:
139*aed3e508SAndroid Build Coastguard Worker      self.bgn_entry_index = self._get_begin_hwstate(bgn_time)
140*aed3e508SAndroid Build Coastguard Worker    else:
141*aed3e508SAndroid Build Coastguard Worker      self.bgn_entry_index = 0
142*aed3e508SAndroid Build Coastguard Worker
143*aed3e508SAndroid Build Coastguard Worker    if end_time is not None:
144*aed3e508SAndroid Build Coastguard Worker      if end_gesture_flag:
145*aed3e508SAndroid Build Coastguard Worker        self.end_entry_index = self._get_end_gesture(end_time)
146*aed3e508SAndroid Build Coastguard Worker      else:
147*aed3e508SAndroid Build Coastguard Worker        self.end_entry_index = self._get_end_entry(end_time)
148*aed3e508SAndroid Build Coastguard Worker    else:
149*aed3e508SAndroid Build Coastguard Worker      self.end_entry_index = len(self.entries) - 1
150*aed3e508SAndroid Build Coastguard Worker
151*aed3e508SAndroid Build Coastguard Worker    if self.bgn_entry_index is None:
152*aed3e508SAndroid Build Coastguard Worker      logging.error('Error: fail to shrink the log baed on begin time: %f' %
153*aed3e508SAndroid Build Coastguard Worker                    bgn_time)
154*aed3e508SAndroid Build Coastguard Worker    if self.end_entry_index is None:
155*aed3e508SAndroid Build Coastguard Worker      logging.error('Error: fail to shrink the log baed on end time: %f' %
156*aed3e508SAndroid Build Coastguard Worker                    end_time)
157*aed3e508SAndroid Build Coastguard Worker    if self.bgn_entry_index is None or self.end_entry_index is None:
158*aed3e508SAndroid Build Coastguard Worker      exit(1)
159*aed3e508SAndroid Build Coastguard Worker
160*aed3e508SAndroid Build Coastguard Worker    self.shrunk_log[self.ENTRIES] = self.entries[self.bgn_entry_index :
161*aed3e508SAndroid Build Coastguard Worker                                                 self.end_entry_index + 1]
162*aed3e508SAndroid Build Coastguard Worker    logging.info('  bgn_entry_index (%d):  %s' %
163*aed3e508SAndroid Build Coastguard Worker                 (self.bgn_entry_index, self.entries[self.bgn_entry_index]))
164*aed3e508SAndroid Build Coastguard Worker    logging.info('  end_entry_index (%d):  %s' %
165*aed3e508SAndroid Build Coastguard Worker                 (self.end_entry_index, self.entries[self.end_entry_index]))
166*aed3e508SAndroid Build Coastguard Worker
167*aed3e508SAndroid Build Coastguard Worker  def replace_properties(self, prop_file):
168*aed3e508SAndroid Build Coastguard Worker    """Replace properties with those in the given file."""
169*aed3e508SAndroid Build Coastguard Worker    if not prop_file:
170*aed3e508SAndroid Build Coastguard Worker      return
171*aed3e508SAndroid Build Coastguard Worker    with open(prop_file) as f:
172*aed3e508SAndroid Build Coastguard Worker      prop = json.load(f)
173*aed3e508SAndroid Build Coastguard Worker    properties = prop.get(self.PROPERTIES)
174*aed3e508SAndroid Build Coastguard Worker    if properties:
175*aed3e508SAndroid Build Coastguard Worker        self.shrunk_log[self.PROPERTIES] = properties
176*aed3e508SAndroid Build Coastguard Worker
177*aed3e508SAndroid Build Coastguard Worker  def add_description(self, description):
178*aed3e508SAndroid Build Coastguard Worker    """Add description to the shrunk log."""
179*aed3e508SAndroid Build Coastguard Worker    if description:
180*aed3e508SAndroid Build Coastguard Worker      self.shrunk_log[self.DESCRIPTION] = description
181*aed3e508SAndroid Build Coastguard Worker
182*aed3e508SAndroid Build Coastguard Worker  def dump_json(self, output_file):
183*aed3e508SAndroid Build Coastguard Worker    """Dump the new log object to a jason file."""
184*aed3e508SAndroid Build Coastguard Worker    with open(output_file, 'w') as f:
185*aed3e508SAndroid Build Coastguard Worker      json.dump(self.shrunk_log, f, indent=3, separators=(',', ': '),
186*aed3e508SAndroid Build Coastguard Worker                sort_keys=True)
187*aed3e508SAndroid Build Coastguard Worker
188*aed3e508SAndroid Build Coastguard Worker  def run(self, options):
189*aed3e508SAndroid Build Coastguard Worker    """Run the operations on the log.
190*aed3e508SAndroid Build Coastguard Worker
191*aed3e508SAndroid Build Coastguard Worker    The operations include shrinking the log and replacing the properties.
192*aed3e508SAndroid Build Coastguard Worker    """
193*aed3e508SAndroid Build Coastguard Worker    logging.info('Log file: %s' % options['log'])
194*aed3e508SAndroid Build Coastguard Worker    self.shrink(bgn_time=options['bgn_time'], end_time=options['end_time'],
195*aed3e508SAndroid Build Coastguard Worker                end_gesture_flag=options['end_gesture'])
196*aed3e508SAndroid Build Coastguard Worker    self.replace_properties(options['prop'])
197*aed3e508SAndroid Build Coastguard Worker    self.add_description(options['description'])
198*aed3e508SAndroid Build Coastguard Worker    self.dump_json(options['output'])
199*aed3e508SAndroid Build Coastguard Worker
200*aed3e508SAndroid Build Coastguard Worker
201*aed3e508SAndroid Build Coastguard Workerdef _usage():
202*aed3e508SAndroid Build Coastguard Worker  """Print the usage of this program."""
203*aed3e508SAndroid Build Coastguard Worker  logging.info('Usage: $ %s [options]\n' % sys.argv[0])
204*aed3e508SAndroid Build Coastguard Worker  logging.info('options:')
205*aed3e508SAndroid Build Coastguard Worker  logging.info('  -b, --begin=<event_begin_time>')
206*aed3e508SAndroid Build Coastguard Worker  logging.info('        the begin timestamp to shrink the log.')
207*aed3e508SAndroid Build Coastguard Worker  logging.info('  -d, --description=<log_description>')
208*aed3e508SAndroid Build Coastguard Worker  logging.info('        Description of the log, e.g., "crosbug.com/12345"')
209*aed3e508SAndroid Build Coastguard Worker  logging.info('  -e, --end=<event_end_time>')
210*aed3e508SAndroid Build Coastguard Worker  logging.info('        the end timestamp to shrink the log.')
211*aed3e508SAndroid Build Coastguard Worker  logging.info('  -g, --end_gesture')
212*aed3e508SAndroid Build Coastguard Worker  logging.info('        When this flag is set, the shrunk log will contain\n'
213*aed3e508SAndroid Build Coastguard Worker               '        the gestures resulted from the activities within the\n'
214*aed3e508SAndroid Build Coastguard Worker               '        time range. Otherwise, the shrunk log will have a\n'
215*aed3e508SAndroid Build Coastguard Worker               '        hard cut at the entry with the smallest timestamp\n'
216*aed3e508SAndroid Build Coastguard Worker               '        greater than or equal to the specified end_time.')
217*aed3e508SAndroid Build Coastguard Worker  logging.info('  -h, --help: show this help')
218*aed3e508SAndroid Build Coastguard Worker  logging.info('  -l, --log=<activity_log> (required)')
219*aed3e508SAndroid Build Coastguard Worker  logging.info('  -o, --output=<output_file> (required)')
220*aed3e508SAndroid Build Coastguard Worker  logging.info('  -p, --prop=<new_property_file>')
221*aed3e508SAndroid Build Coastguard Worker  logging.info('        If a new property file is specified, it will be used\n'
222*aed3e508SAndroid Build Coastguard Worker               '        to replace the original properties in the log.')
223*aed3e508SAndroid Build Coastguard Worker  logging.info('')
224*aed3e508SAndroid Build Coastguard Worker
225*aed3e508SAndroid Build Coastguard Worker
226*aed3e508SAndroid Build Coastguard Workerdef _parse_options():
227*aed3e508SAndroid Build Coastguard Worker  """Parse the command line options."""
228*aed3e508SAndroid Build Coastguard Worker  try:
229*aed3e508SAndroid Build Coastguard Worker    short_opt = 'b:d:e:ghl:o:p:'
230*aed3e508SAndroid Build Coastguard Worker    long_opt = ['begin=', 'description', 'end=', 'end_gesture', 'help',
231*aed3e508SAndroid Build Coastguard Worker                'log=', 'output=', 'prop=']
232*aed3e508SAndroid Build Coastguard Worker    opts, _ = getopt.getopt(sys.argv[1:], short_opt, long_opt)
233*aed3e508SAndroid Build Coastguard Worker  except getopt.GetoptError, err:
234*aed3e508SAndroid Build Coastguard Worker    logging.error('Error: %s' % str(err))
235*aed3e508SAndroid Build Coastguard Worker    _usage()
236*aed3e508SAndroid Build Coastguard Worker    sys.exit(1)
237*aed3e508SAndroid Build Coastguard Worker
238*aed3e508SAndroid Build Coastguard Worker  options = {}
239*aed3e508SAndroid Build Coastguard Worker  options['end_gesture'] = False
240*aed3e508SAndroid Build Coastguard Worker  options['bgn_time'] = None
241*aed3e508SAndroid Build Coastguard Worker  options['description'] = None
242*aed3e508SAndroid Build Coastguard Worker  options['end_time'] = None
243*aed3e508SAndroid Build Coastguard Worker  options['prop'] = None
244*aed3e508SAndroid Build Coastguard Worker  for opt, arg in opts:
245*aed3e508SAndroid Build Coastguard Worker    if opt in ('-h', '--help'):
246*aed3e508SAndroid Build Coastguard Worker      _usage()
247*aed3e508SAndroid Build Coastguard Worker      sys.exit()
248*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-b', '--begin'):
249*aed3e508SAndroid Build Coastguard Worker      options['bgn_time'] = float(arg)
250*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-d', '--description'):
251*aed3e508SAndroid Build Coastguard Worker      options['description'] = arg
252*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-e', '--end'):
253*aed3e508SAndroid Build Coastguard Worker      options['end_time'] = float(arg)
254*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-g', '--end_gesture'):
255*aed3e508SAndroid Build Coastguard Worker      options['end_gesture'] = True
256*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-l', '--log'):
257*aed3e508SAndroid Build Coastguard Worker      if os.path.isfile(arg):
258*aed3e508SAndroid Build Coastguard Worker        options['log'] = arg
259*aed3e508SAndroid Build Coastguard Worker      else:
260*aed3e508SAndroid Build Coastguard Worker        logging.error('Error: the log file does not exist: %s.' % arg)
261*aed3e508SAndroid Build Coastguard Worker        sys.exit(1)
262*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-o', '--output'):
263*aed3e508SAndroid Build Coastguard Worker      options['output'] = arg
264*aed3e508SAndroid Build Coastguard Worker    elif opt in ('-p', '--prop'):
265*aed3e508SAndroid Build Coastguard Worker      if os.path.isfile(arg):
266*aed3e508SAndroid Build Coastguard Worker        options['prop'] = arg
267*aed3e508SAndroid Build Coastguard Worker      else:
268*aed3e508SAndroid Build Coastguard Worker        logging.error('Error: the properties file does not exist: %s.' % arg)
269*aed3e508SAndroid Build Coastguard Worker        sys.exit(1)
270*aed3e508SAndroid Build Coastguard Worker    else:
271*aed3e508SAndroid Build Coastguard Worker      logging.error('Error: This option %s is not handled in program.' % opt)
272*aed3e508SAndroid Build Coastguard Worker      _usage()
273*aed3e508SAndroid Build Coastguard Worker      sys.exit(1)
274*aed3e508SAndroid Build Coastguard Worker
275*aed3e508SAndroid Build Coastguard Worker  if not options.get('log') or not options.get('output'):
276*aed3e508SAndroid Build Coastguard Worker    logging.error('Error: You need to specify both --log and --output.')
277*aed3e508SAndroid Build Coastguard Worker    _usage()
278*aed3e508SAndroid Build Coastguard Worker    sys.exit(1)
279*aed3e508SAndroid Build Coastguard Worker  return options
280*aed3e508SAndroid Build Coastguard Worker
281*aed3e508SAndroid Build Coastguard Worker
282*aed3e508SAndroid Build Coastguard Workerif __name__ == '__main__':
283*aed3e508SAndroid Build Coastguard Worker  logging.basicConfig(format='', level=logging.INFO)
284*aed3e508SAndroid Build Coastguard Worker  options = _parse_options()
285*aed3e508SAndroid Build Coastguard Worker  tplog = TPLog(options['log'])
286*aed3e508SAndroid Build Coastguard Worker  tplog.run(options)
287