1#!/usr/bin/env python3 2 # 3 # Copyright (C) 2016 The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 # 17import argparse 18import json 19import os 20 21# This script parses a profile .html report that was generated by Gradle and generates a machine-readable summary 22 23def main(): 24 parser = argparse.ArgumentParser( 25 description = "Parses a performance profile file generated by Gradle" 26 ) 27 parser.add_argument("--input-profile", required=True, dest="input_profile") 28 parser.add_argument("--output-summary", required=True, dest="output_summary") 29 args = parser.parse_args() 30 summarize(args.input_profile, args.output_summary) 31 32def summarize(inputProfilePath, outputSummaryPath): 33 # mapping from the key in the Gradle report to the key in the summary that we generate 34 mapping = {"Total Build Time": "task_execution_duration", "Task Execution": "total_cpu", "Configuring Projects": "configuration_duration"} 35 parsedValues = parse(inputProfilePath, mapping.keys()) 36 outputValues = dict() 37 for k in mapping: 38 if k in parsedValues: 39 outputValues[mapping[k]] = parsedValues[k] 40 else: 41 raise Exception("Did not find key " + k + " in " + inputProfilePath + "; only found " + str(parsedValues)) 42 os.makedirs(os.path.dirname(outputSummaryPath), exist_ok=True) 43 with open(outputSummaryPath, 'w') as outputFile: 44 json.dump(outputValues, outputFile, sort_keys=True) 45 print("Generated " + outputSummaryPath) 46 47# Parses inputProfilePath into keys and values, and returns a dict whose keys match interestingKeys 48def parse(inputProfilePath, interestingKeys): 49 with open(inputProfilePath) as inputProfile: 50 values = dict() 51 currentKey = None 52 for line in inputProfile.readlines(): 53 line = line.strip() 54 lineText = line.replace("<td>", "").replace('<td class="numeric">', "").replace("</td>", "") 55 if currentKey is not None: 56 values[currentKey] = parseDurationSeconds(lineText) * 1000 57 if lineText in interestingKeys: 58 currentKey = lineText 59 else: 60 currentKey = None 61 return values 62 63# Given a duration such as 1h20m02.5s, returns a number of seconds like ((1*60)+20)*60+2=4802.5 64def parseDurationSeconds(durationText): 65 originalDurationText = durationText 66 secondsText = "0" 67 minutesText = "0" 68 hoursText = "0" 69 daysText = "0" 70 if "d" in durationText: 71 daysText, durationText = durationText.split("d") 72 if "h" in durationText: 73 hoursText, durationText = durationText.split("h") 74 if "m" in durationText: 75 minutesText, durationText = durationText.split("m") 76 if "s" in durationText: 77 secondsText, durationText = durationText.split("s") 78 if durationText != "": 79 raise Exception("Failed to parse '" + durationText + "'") 80 try: 81 return (((float(daysText) * 24 + float(hoursText)) * 60) + float(minutesText)) * 60 + float(secondsText) 82 except ValueError as e: 83 raise ValueError("Failed to parse '" + durationText + "'") 84 85if __name__ == "__main__": 86 main() 87