crashclouseau/inspector.py (108 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/.
from libmozdata import socorro
import re
from . import java, tools, utils
from .logger import logger
# Mercurial URI
HG_PAT = re.compile("hg:hg.mozilla.org[^:]*:([^:]*):([a-z0-9]+)")
def get_crash_data(uuid):
"""Get the crash data from Socorro"""
data = socorro.ProcessedCrash.get_processed(uuid)
return data[uuid]
def get_crash(uuid, buildid, channel, mindate, chgset, filelog, interesting_chgsets):
"""Get the a crash with its uuid"""
logger.info("Get {} for analyzis".format(uuid))
data = get_crash_data(uuid)
return get_crash_info(
data, uuid, buildid, channel, mindate, chgset, filelog, interesting_chgsets
)
def get_crash_by_uuid(uuid, mindate, filelog):
"""Get the a crash with its uuid"""
logger.info("Get {} for analyzis".format(uuid))
data = get_crash_data(uuid)
buildid = data["build"]
bid = utils.get_build_date(buildid)
channel = data["release_channel"]
interesting_chgsets = set()
chgset = tools.get_changeset(bid, channel, data["product"])
res = get_crash_info(
data, uuid, bid, channel, mindate, chgset, filelog, interesting_chgsets
)
return res, channel, interesting_chgsets
def get_crash_info(
data, uuid, buildid, channel, mindate, chgset, filelog, interesting_chgsets
):
"""Inspect the crash stack (Java's one too if present)"""
res = {}
java_st = data.get("java_stack_trace")
jframes, files = java.inspect_java_stacktrace(java_st, chgset)
if jframes:
files = filelog(files, mindate, buildid, channel)
if amend(jframes, files, interesting_chgsets):
res["java"] = {"frames": jframes, "hash": get_simplified_hash(jframes)}
else:
if "json_dump" not in data:
return None
frames, files = inspect_stacktrace(data, chgset)
if frames:
files = filelog(files, mindate, buildid, channel)
if amend(frames, files, interesting_chgsets):
res["nonjava"] = {"frames": frames, "hash": get_simplified_hash(frames)}
return res
def get_simplified_hash(frames):
"""Get a hash from the frames we have in the crash stack"""
res = ""
for frame in frames:
if frame["line"] != -1:
res += str(frame["stackpos"]) + "\n"
res += frame["filename"] + "\n"
res += str(frame["line"]) + "\n"
if res != "":
return utils.hash(res)
return ""
def get_path_node(uri):
"""Get the file path and the hg node"""
name = node = ""
if uri:
m = HG_PAT.match(uri)
if m:
name = m.group(1)
node = utils.short_rev(m.group(2))
return name, node
def inspect_stacktrace(data, build_node):
"""Inspect the stack from the data and the check that the hg node
from the build is the same that the one we have in stack data
(the nodes could be different when the crash was occuring during an update)"""
res = []
files = set()
dump = data["json_dump"]
max_frames = 50
if "threads" in dump:
N = dump["crash_info"].get("crashing_thread")
if N is not None:
frames = dump["threads"][N]["frames"]
frames = frames[0:max_frames]
for n, frame in enumerate(frames):
uri = frame.get("file")
filename, node = get_path_node(uri)
if node:
if node != build_node:
return [], set()
files.add(filename)
fun = frame.get("function", "")
line = frame.get("line", -1)
module = frame.get("module", "")
res.append(
{
"original": uri,
"filename": filename,
"changesets": [],
"module": module,
"function": fun,
"line": line,
"node": node,
"internal": node != "",
"stackpos": n,
}
)
return res, files
def amend(frames, files, interesting_chgsets):
"""Amend frame info"""
interesting = False
if files:
for frame in frames:
filename = frame["filename"]
if filename in files:
chgsets = files[filename]
interesting_chgsets |= set(chgsets)
frame["changesets"] = chgsets
interesting = True
return interesting