tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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()