crashclouseau/java.py (142 lines of code) (raw):

# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. import html from libmozdata.hgmozilla import Mercurial import re import requests import time from . import models, tools # must match 'at android.os.Parcel.readException(Parcel.java:1552)' JAVA_PAT1 = re.compile(r"^at\ ([^\(]+)\(([^:]+):([0-9]*)\)$") # must match $123 in MyClass$123 or MyClass$Inner JAVA_PAT2 = re.compile(r"\$.*") JAVA_PAT3 = re.compile(r"\([^:]+:[0-9]*\)$") GITHUB_URL = "https://api.github.com/repos/mozilla/gecko-dev" def parse_path(path): path = path.split(".") method = path[-1] # we remove the method and the class name path = path[:-2] # remove inner class stuff \$.* path = map(lambda x: JAVA_PAT2.sub("", x), path) path = "/".join(path) return path, method def inspect_java_stacktrace(st, node, get_full_path=models.File.get_full_path): if not st: return [], set() lines = map(lambda x: x.strip(), st.split("\n")) lines = filter(lambda x: x.startswith("at "), lines) stack = [] files = set() for n, line in enumerate(lines): m = JAVA_PAT1.match(line) d = { "original": line, "filename": "", "module": "", "changesets": [], "function": "", "node": "", "line": 0, "internal": False, "stackpos": n, } stack.append(d) if m: path, filename, linenumber = m.groups() if path.startswith("org.mozilla."): d["internal"] = True d["node"] = node d["line"] = int(linenumber) base_path, d["function"] = parse_path(path) filename = base_path + "/" + filename filename = get_full_path(filename) d["filename"] = filename files.add(filename) return stack, files def reformat_java_stacktrace( st, channel, buildid, get_full_path=models.File.get_full_path, get_changeset=tools.get_changeset, ): if not st: return "" node = get_changeset(buildid, channel, "FennecAndroid") if not node: return html.escape(st) res = "" repo_url = Mercurial.get_repo_url(channel) lines = list(st.split("\n")) N = len(lines) for i in range(N): line = lines[i] m = JAVA_PAT1.match(line.strip()) line = html.escape(line) added = False if m: path, filename, linenumber = m.groups() if path.startswith("org.mozilla."): base_path, _ = parse_path(path) repo_filename = base_path + "/" + filename repo_filename = get_full_path(repo_filename) r = '(<a href="{}/annotate/{}/{}#l{}">{}:{}</a>)' r = r.format( repo_url, node, repo_filename, linenumber, filename, linenumber ) res += JAVA_PAT3.sub(r, line) added = True if not added: res += line if i < N - 1: res += "\n" return res def get_sha(path, filename, sleep=0.1, retry=10): url = "{}/contents/{}".format(GITHUB_URL, path) for _ in range(retry): r = requests.get(url) if r.status_code == 200: for data in r.json(): if data["name"] == filename: return data["sha"] raise Exception("Cannot get GitHub sha for {}/{}".format(path, filename)) else: time.sleep(sleep) raise Exception("Too many attempts in java.get_sha (retry={})".format(retry)) def get_java_files(root, sha, sleep=0.1, retry=10): url = "{}/git/trees/{}?recursive=1".format(GITHUB_URL, sha) for _ in range(retry): r = requests.get(url) if r.status_code == 200: res = [] for data in r.json()["tree"]: path = data["path"] if path.endswith(".java"): res.append(root + "/" + path) return res else: time.sleep(sleep) raise Exception("Too many attempts in java.get_java_files (retry={})".format(retry)) def get_all_java_files(sleep=0.1, retry=10): # first we get the sha of directory mobile/android sha = get_sha("mobile", "android") # get the java files in the dir corresponding to the sha files = get_java_files("mobile/android", sha) return files def populate_java_files(): # We need to have all the java files to be able to build urls from java crash stack # We get them in using the GitHub API (since I didn't find out any good solution in using Mercurial one) files = get_all_java_files() models.File.populate(files) def write_java_stack(uuid, path): import json from libmozdata import socorro data = socorro.ProcessedCrash.get_processed(uuid) data = data[uuid] channel = data["release_channel"] buildid = data["build"] java_st = data.get("java_stack_trace") jframes, files = inspect_java_stacktrace(java_st, "tip") reformatted = reformat_java_stacktrace(java_st, channel, buildid) res = { "stack": java_st, "frames": jframes, "files": list(sorted(files)), "uuid": uuid, "reformatted": reformatted, "channel": channel, "buildid": buildid, } with open(path, "w") as Out: json.dump(res, Out) # write_java_stack('52b6dc27-6755-4ed5-8bfa-68d050180201', './tests/java/stack.1.json') # write_java_stack('bdf532de-40ec-446d-bf55-5c4550180201', './tests/java/stack.2.json')