analyze.py (5129B)
1 #!/usr/bin/env python3 2 # This Source Code Form is subject to the terms of the Mozilla Public 3 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 import sys 7 from os import path 8 9 import click 10 11 from qm_try_analysis import fn_anchors, stackanalysis, utils 12 from qm_try_analysis.logging import error, info 13 14 """ 15 The analysis is based on stack frames of the following form: 16 17 [ 18 { 19 "event_timeabs": 1617121013137, 20 "session_startabs": 1617120840000, 21 "build_id": "20210329095128", 22 "client_id": "0013a68f-9893-461a-93d4-2d7a2f85583f", 23 "session_id": "8cd37159-bd5c-481c-99ad-9eace9ea726a", 24 "seq": 1, 25 "context": "Initialization::TemporaryStorage", 26 "source_file": "dom/localstorage/ActorsParent.cpp", 27 "source_line": "1018", 28 "severity": "ERROR", 29 "result": "NS_ERROR_FILE_NOT_FOUND" 30 }, 31 ... 32 ] 33 34 The location of the input file is expected to be found in the 35 last item of the list inside qmexecutions.json. 36 """ 37 38 39 @click.command() 40 @click.option( 41 "--output-to", 42 type=click.Path(dir_okay=False, writable=True), 43 default="qmstacks_until_<lasteventtime>.txt", 44 help="Specify the output file for the analyzed data.", 45 ) 46 @click.option( 47 "-w", 48 "--workdir", 49 type=click.Path(file_okay=False, exists=True, writable=True), 50 default="output", 51 help="Working directory", 52 ) 53 def analyze_qm_failures(output_to, workdir): 54 """ 55 Analyzes the results from fetch's JSON file. 56 Writes out several JSON results as files and a bugzilla markup table on stdout. 57 """ 58 run = utils.getLastRunFromExecutionFile(workdir) 59 if "numrows" not in run or run["numrows"] == 0: 60 error( 61 "No previous execution from fetch_qm_failures.py found or the last execution yielded no result." 62 ) 63 sys.exit(2) 64 65 if output_to == "qmstacks_until_<lasteventtime>.txt": 66 output_to = path.join(workdir, f"qmstacks_until_{run['lasteventtime']}.txt") 67 elif output_to.exists(): 68 error( 69 f'The output file "{output_to}" already exists. This script would override it.' 70 ) 71 sys.exit(2) 72 run["stacksfile"] = output_to 73 74 def getFname(prefix): 75 return "{}/{}_until_{}.json".format(workdir, prefix, run["lasteventtime"]) 76 77 # read rows from JSON 78 rows = utils.readJSONFile(getFname("qmrows")) 79 info(f"Found {len(rows)} rows of data") 80 rows = stackanalysis.sanitize(rows) 81 82 # enrich rows with hg locations 83 buildids = stackanalysis.extractBuildIDs(rows) 84 utils.fetchBuildRevisions(buildids) 85 stackanalysis.constructHGLinks(buildids, rows) 86 87 # transform rows to unique stacks 88 raw_stacks = stackanalysis.collectRawStacks(rows) 89 all_stacks = stackanalysis.mergeEqualStacks(raw_stacks) 90 91 # enrich with function anchors 92 for stack in all_stacks: 93 for frame in stack["frames"]: 94 frame["anchor"] = "{}:{}".format( 95 frame["source_file"], fn_anchors.getFunctionName(frame["location"]) 96 ) 97 98 # separate stacks for relevance 99 error_stacks = [] 100 warn_stacks = [] 101 info_stacks = [] 102 abort_stacks = [] 103 stackanalysis.filterStacksForPropagation( 104 all_stacks, error_stacks, warn_stacks, info_stacks, abort_stacks 105 ) 106 run["errorfile"] = getFname("qmerrors") 107 utils.writeJSONFile(run["errorfile"], error_stacks) 108 run["warnfile"] = getFname("qmwarnings") 109 utils.writeJSONFile(run["warnfile"], warn_stacks) 110 run["infofile"] = getFname("qminfo") 111 utils.writeJSONFile(run["infofile"], info_stacks) 112 run["abortfile"] = getFname("qmabort") 113 utils.writeJSONFile(run["abortfile"], abort_stacks) 114 utils.updateLastRunToExecutionFile(workdir, run) 115 116 info(f"Found {len(error_stacks)} error stacks") 117 info(f"Found {len(warn_stacks)} warning stacks") 118 info(f"Found {len(info_stacks)} info stacks") 119 info(f"Found {len(abort_stacks)} aborted stacks") 120 121 # Write results to the specified output file 122 with open(output_to, "w") as output: 123 124 def print_to_output(message): 125 print(message, file=output) 126 127 print_to_output("Error stacks:") 128 print_to_output(stackanalysis.printStacks(error_stacks)) 129 print_to_output("") 130 print_to_output("Error stacks grouped by anchors:") 131 anchors = stackanalysis.groupStacksForAnchors(error_stacks) 132 anchornames = list(anchors.keys()) 133 for a in anchornames: 134 print_to_output(stackanalysis.printStacks(anchors[a]["stacks"])) 135 print_to_output("") 136 print_to_output("") 137 print_to_output("Warning stacks:") 138 print_to_output(stackanalysis.printStacks(warn_stacks)) 139 print_to_output("") 140 print_to_output("Info stacks:") 141 print_to_output(stackanalysis.printStacks(info_stacks)) 142 print_to_output("") 143 print_to_output("Aborted stacks:") 144 print_to_output(stackanalysis.printStacks(abort_stacks)) 145 146 info(f"Wrote results to specified output file {output_to}") 147 148 149 if __name__ == "__main__": 150 analyze_qm_failures()