forked from getsentry/coredump-uploader
Update upload-coredump.py
This commit is contained in:
@ -11,12 +11,18 @@ import time
|
|||||||
|
|
||||||
class Frame:
|
class Frame:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, instruction_addr="", name_of_function="", path="abs_path", lineno=None,
|
self,
|
||||||
|
instruction_addr=None,
|
||||||
|
function=None,
|
||||||
|
filename="abs_path",
|
||||||
|
lineno=None,
|
||||||
|
package=None,
|
||||||
):
|
):
|
||||||
self.instruction_addr = instruction_addr
|
self.instruction_addr = instruction_addr
|
||||||
self.name_of_function = name_of_function
|
self.function = function
|
||||||
self.path = path
|
self.filename = filename
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
|
self.package = package
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return self.__dict__
|
return self.__dict__
|
||||||
@ -43,37 +49,64 @@ class Image:
|
|||||||
return self.__dict__
|
return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
class Stacktrace_for_thread:
|
||||||
|
def __init__(self):
|
||||||
|
self.frames = []
|
||||||
|
|
||||||
|
def append_frame(self, frame=None):
|
||||||
|
self.frames.append(frame)
|
||||||
|
|
||||||
|
def reverse_list(self):
|
||||||
|
self.frames.reverse()
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
class Thread:
|
||||||
|
def __init__(self, id="", name=None, crashed=None, frames=None):
|
||||||
|
self.stacktrace = {}
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.crashed = crashed
|
||||||
|
self.stacktrace = frames
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
_frame_re = re.compile(
|
_frame_re = re.compile(
|
||||||
r"""(?x)
|
r"""(?xim)
|
||||||
|
^
|
||||||
#address of instruction
|
# frame number
|
||||||
(?P<instruction_addr>
|
\#\d+\s+
|
||||||
0[xX][a-fA-F0-9]+
|
# instruction address (missing for first frame)
|
||||||
)
|
(?:
|
||||||
|
(?P<instruction_addr>0x[0-9a-f]+)
|
||||||
#name of function
|
\sin\s
|
||||||
(\sin)?
|
|
||||||
\s?
|
|
||||||
(.*::)?
|
|
||||||
(?P<name_of_function>
|
|
||||||
[a-zA-z]+
|
|
||||||
)?
|
)?
|
||||||
|
# function name (?? if unknown)
|
||||||
|
(?P<function>.*)
|
||||||
|
\s
|
||||||
|
# arguments, ignored
|
||||||
|
\([^)]*\)
|
||||||
|
|
||||||
#path from the file
|
\s*
|
||||||
(\s\(.*\))? (\sat\s)?
|
(?:
|
||||||
(?P<path>
|
# package name, without debug info
|
||||||
.*\.c
|
from\s
|
||||||
|
(?P<package>[^ ]+)
|
||||||
|
|
|
||||||
|
# file name and line number
|
||||||
|
at\s
|
||||||
|
(?P<filename>[^ ]+)
|
||||||
|
:
|
||||||
|
(?P<lineno>\d+)
|
||||||
)?
|
)?
|
||||||
|
$
|
||||||
#Number of the line
|
"""
|
||||||
:?
|
|
||||||
(?P<lineno>
|
|
||||||
[0-9]+
|
|
||||||
)*
|
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_image_re = re.compile(
|
_image_re = re.compile(
|
||||||
r"""(?x)
|
r"""(?x)
|
||||||
|
|
||||||
@ -108,7 +141,7 @@ _image_re = re.compile(
|
|||||||
(?P<code_file>
|
(?P<code_file>
|
||||||
[\/|\/][\w|\S]+|\S+\.\S+|[a-zA-Z]*
|
[\/|\/][\w|\S]+|\S+\.\S+|[a-zA-Z]*
|
||||||
)?
|
)?
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -121,22 +154,21 @@ def error(message):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def get_frame(gdb_output):
|
def get_frame(temp):
|
||||||
"""Parses the output from gdb """
|
""" """
|
||||||
frame = Frame()
|
frame = Frame()
|
||||||
temp = _frame_re.search(gdb_output)
|
|
||||||
if temp is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
frame.instruction_addr = temp.group("instruction_addr")
|
frame.instruction_addr = temp.group("instruction_addr")
|
||||||
|
|
||||||
frame.name_of_function = temp.group("name_of_function")
|
frame.function = temp.group("function")
|
||||||
|
|
||||||
if temp.group("lineno") is not None:
|
if temp.group("lineno") is not None:
|
||||||
frame.lineno = int(temp.group("lineno"))
|
frame.lineno = int(temp.group("lineno"))
|
||||||
|
|
||||||
if temp.group("path") is not None:
|
if temp.group("filename") is not None:
|
||||||
frame.path = temp.group("path")
|
frame.filename = temp.group("filename")
|
||||||
|
|
||||||
|
if temp.group("package") is not None:
|
||||||
|
frame.package = temp.group("package")
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
@ -157,14 +189,82 @@ def get_image(image_string):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_threads(path_to_core, path_to_executable, gdb_path):
|
||||||
|
|
||||||
|
thread_list = []
|
||||||
|
counter_threads = 0
|
||||||
|
|
||||||
|
# execute gdb
|
||||||
|
if gdb_path is None:
|
||||||
|
process = subprocess.Popen(
|
||||||
|
["gdb", "-c", path_to_core, path_to_executable],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(
|
||||||
|
[gdb_path, "gdb", "-c", path_to_core, path_to_executable],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
except OSError as err:
|
||||||
|
error(err)
|
||||||
|
|
||||||
|
output, errors = process.communicate(input="thread apply all bt")
|
||||||
|
if errors:
|
||||||
|
error(errors)
|
||||||
|
|
||||||
|
gdb_output = output.decode("utf-8")
|
||||||
|
|
||||||
|
# splits gdb output to get each Thread
|
||||||
|
try:
|
||||||
|
gdb_output = output.split("Thread")
|
||||||
|
except:
|
||||||
|
error("gdb output error")
|
||||||
|
|
||||||
|
del gdb_output[0]
|
||||||
|
gdb_output.reverse()
|
||||||
|
|
||||||
|
for thread_backtrace in gdb_output:
|
||||||
|
stacktrace = Stacktrace_for_thread()
|
||||||
|
|
||||||
|
# gets the Thread ID
|
||||||
|
thread_id = re.search(r"(?x)LWP\s(?P<thread_id>[a-fA-F0-9]+)", thread_backtrace)
|
||||||
|
|
||||||
|
# gets each frame from the Thread
|
||||||
|
for match in re.finditer(_frame_re, thread_backtrace):
|
||||||
|
frame = get_frame(match)
|
||||||
|
if frame is not None:
|
||||||
|
stacktrace.append_frame(frame.to_json())
|
||||||
|
|
||||||
|
stacktrace.reverse_list()
|
||||||
|
|
||||||
|
if counter_threads == 0:
|
||||||
|
crashed = True
|
||||||
|
else:
|
||||||
|
crashed = False
|
||||||
|
|
||||||
|
# appends a Thread to the thread_list
|
||||||
|
thread_list.append(
|
||||||
|
Thread(thread_id.group("thread_id"), None, crashed, stacktrace.to_json())
|
||||||
|
)
|
||||||
|
counter_threads += 1
|
||||||
|
|
||||||
|
print("Threads found: " + str(counter_threads))
|
||||||
|
return thread_list
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.argument("path_to_core")
|
@click.argument("path_to_core")
|
||||||
@click.argument("path_to_executable")
|
@click.argument("path_to_executable")
|
||||||
@click.option("--sentry-dsn", required=False)
|
@click.option("--sentry-dsn", required=False)
|
||||||
@click.option("--gdb-path", required=False)
|
@click.option("--gdb-path", required=False)
|
||||||
@click.option("--elfutils-path", required=False)
|
@click.option("--elfutils-path", required=False)
|
||||||
def main(path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path):
|
@click.option("--all-threads", is_flag=True)
|
||||||
|
def main(
|
||||||
|
path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path, all_threads
|
||||||
|
):
|
||||||
# Validate input Path
|
# Validate input Path
|
||||||
if os.path.isfile(path_to_core) is not True:
|
if os.path.isfile(path_to_core) is not True:
|
||||||
error("Wrong path to coredump")
|
error("Wrong path to coredump")
|
||||||
@ -181,9 +281,6 @@ def main(path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path):
|
|||||||
image_list = []
|
image_list = []
|
||||||
frame_list = []
|
frame_list = []
|
||||||
|
|
||||||
gdb_output = []
|
|
||||||
eu_unstrip_output = []
|
|
||||||
|
|
||||||
# execute gdb
|
# execute gdb
|
||||||
if gdb_path is None:
|
if gdb_path is None:
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
@ -201,12 +298,21 @@ def main(path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path):
|
|||||||
except OSError as err:
|
except OSError as err:
|
||||||
error(format(err))
|
error(format(err))
|
||||||
|
|
||||||
output = re.search(r"#0.*", str(process.communicate(input="bt")))
|
output, errors = process.communicate(input="bt")
|
||||||
try:
|
if errors:
|
||||||
gdb_output = output.group().split("#")
|
error(errors)
|
||||||
except:
|
|
||||||
|
gdb_output = output.decode("utf-8")
|
||||||
|
if not "#0" in gdb_output:
|
||||||
error("gdb output error")
|
error("gdb output error")
|
||||||
|
|
||||||
|
try:
|
||||||
|
type_of_event = re.search(
|
||||||
|
r"terminated with signal (?P<type>.*),", gdb_output
|
||||||
|
).group("type")
|
||||||
|
except:
|
||||||
|
type_of_event = "Core"
|
||||||
|
|
||||||
# execute eu-unstrip
|
# execute eu-unstrip
|
||||||
if elfutils_path is None:
|
if elfutils_path is None:
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
@ -234,8 +340,8 @@ def main(path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path):
|
|||||||
|
|
||||||
eu_unstrip_output = str(output[0]).split("\n")
|
eu_unstrip_output = str(output[0]).split("\n")
|
||||||
|
|
||||||
for x in range(1, len(gdb_output)):
|
for match in re.finditer(_frame_re, gdb_output):
|
||||||
frame = get_frame(gdb_output[x])
|
frame = get_frame(match)
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
frame_list.append(frame)
|
frame_list.append(frame)
|
||||||
|
|
||||||
@ -244,20 +350,36 @@ def main(path_to_core, path_to_executable, sentry_dsn, gdb_path, elfutils_path):
|
|||||||
if image is not None:
|
if image is not None:
|
||||||
image_list.append(image)
|
image_list.append(image)
|
||||||
|
|
||||||
|
frame_list.reverse()
|
||||||
|
type_of_event = "Core"
|
||||||
# build the json for sentry
|
# build the json for sentry
|
||||||
|
if all_threads:
|
||||||
|
thread_list = get_all_threads(path_to_core, path_to_executable, gdb_path)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"platform": "native",
|
"platform": "native",
|
||||||
"exception": {
|
"exception": {
|
||||||
"type": "Core",
|
"type": type_of_event,
|
||||||
"handled": "false",
|
"mechanism": {"type": "coredump", "handled": False, "synthetic": True},
|
||||||
|
"stacktrace": {"frames": [ob.to_json() for ob in frame_list]},
|
||||||
|
},
|
||||||
|
"debug_meta": {"images": [ob.to_json() for ob in image_list]},
|
||||||
|
"threads": {"values": [ob.to_json() for ob in thread_list]},
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"platform": "native",
|
||||||
|
"exception": {
|
||||||
|
"type": type_of_event,
|
||||||
|
"mechanism": {"type": "coredump", "handled": False, "synthetic": True},
|
||||||
"stacktrace": {"frames": [ob.to_json() for ob in frame_list]},
|
"stacktrace": {"frames": [ob.to_json() for ob in frame_list]},
|
||||||
},
|
},
|
||||||
"debug_meta": {"images": [ob.to_json() for ob in image_list]},
|
"debug_meta": {"images": [ob.to_json() for ob in image_list]},
|
||||||
}
|
}
|
||||||
|
|
||||||
sentry_sdk.init(sentry_dsn)
|
sentry_sdk.init(sentry_dsn)
|
||||||
sentry_sdk.capture_event(data)
|
event_id = sentry_sdk.capture_event(data)
|
||||||
print("Core dump sent to sentry!")
|
print("Core dump sent to sentry: %s" % (event_id,))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Reference in New Issue
Block a user