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