From 45738c5b4650f28d832c541546b6d40b9d26c426 Mon Sep 17 00:00:00 2001 From: drindhauser <57894940+drindhauser@users.noreply.github.com> Date: Fri, 6 Dec 2019 15:31:45 +0100 Subject: [PATCH] Add files via upload --- LICENSE | 20 +++++ Makefile | 10 +++ README.md | 24 +++++ upload-coredump.py | 218 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 upload-coredump.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b682cca --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2d322d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +test: + @pytest tests + +.PHONY: test + +help: + @echo "Usage: upload-coredump.py [path to core] [path to executable]" + @echo "" + +.PHONY: help \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcb3689 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +Coredump Uploader +========= + +Requirements: +------- + +GDB + +elfutils + + +Usage: +-------- + +```` +$ export SENTRY_DSN=https://something@your-sentry-dsn/42 +$ python upload-coredump.py /path/to/core /path/to/executable +```` + +OR + +```` +$ python upload-coredump.py /path/to/core /path/to/executable https://something@your-sentry-dsn/42 +```` \ No newline at end of file diff --git a/upload-coredump.py b/upload-coredump.py new file mode 100644 index 0000000..9bb4854 --- /dev/null +++ b/upload-coredump.py @@ -0,0 +1,218 @@ +import re +import sentry_sdk +import binascii +import uuid +import subprocess +import sys +import os +import click + + +class Frame: + def __init__(self, instruction_addr="", name_of_function="", path="", lineno=""): + self.instruction_addr = instruction_addr + self.name_of_function = name_of_function + self.path = path + self.lineno = lineno + + def to_json(self): + return self.__dict__ + + +class Image: + def __init__( + self, + type="", + image_addr="", + image_size="", + debug_id="", + code_id="", + code_file="", + ): + self.type = type + self.image_addr = image_addr + self.image_size = image_size + self.debug_id = debug_id + self.code_id = code_id + self.code_file = code_file + + def to_json(self): + return self.__dict__ + + +_frame_re = re.compile( + r"""(?x) + + #address of instruction + (?P + 0[xX][a-fA-F0-9]+ + )* + + #name of function + \sin\s + (?P + .* + ) + + #path from the file + \s\(\)(\sat\s)? + (?P + .*\.c + )? + #Number of the line + :? + (?P + [0-9]+ + )* +""" +) + + +_image_re = re.compile( + r"""(?x) + + # address of image + (?P + 0[xX][a-fA-F0-9]+ + )* + + # size of image + \+ + (?P + 0[xX][a-fA-F0-9]+ + )* + + # code ID + \s + (?P + [0-9A-Fa-f]+ + )* + + # other address of image? + @ + (?: + 0[xX][0-9A-F-a-f]+ + )* + + #Code file + (\s|\s\.\s\-\s) + (?P + [\/|.\/][\w|\S]+|\S+\.\S+ + ) +""" +) + + +def code_id_to_debug_id(code_id): + return str(uuid.UUID(bytes_le=binascii.unhexlify(code_id)[:16])) + + +def error(message): + print("error: {}".format(message)) + sys.exit(1) + + +def get_frame(gdb_output): + """Parses the output from gdb """ + + frame = Frame() + temp = _frame_re.search(gdb_output) + if temp is not None: + frame.instruction_addr = temp.group("instruction_addr") + frame.name_of_function = temp.group("name_of_function") + frame.lineno = temp.group("lineno") + if temp.group("path") is None: + frame.path = "abs_path" + else: + frame.path = temp.group("path") + + return frame + + +def get_image(image_string): + """Parses the output from eu-unstrip""" + image = Image() + image.type = "elf" + + temp = _image_re.search(image_string) + if temp is not None: + image.image_addr = temp.group("image_addr") + image.image_size = int(temp.group("image_size"), 16) + image.code_id = temp.group("code_id") + image.debug_id = code_id_to_debug_id(temp.group("code_id")) + image.code_file = temp.group("code_file") + + return image + + +@click.command() +@click.argument("path_to_core") +@click.argument("path_to_executable") +@click.argument("sentry_dsn", nargs=-1, required=False) +def main(path_to_core, path_to_executable, sentry_dsn): + + # Validate input Path + if os.path.isfile(path_to_core) is not True: + error("Wrong path to coredump") + + if os.path.isfile(path_to_executable) is not True: + error("Wrong path to executable") + + image_list = [] + frame_list = [] + + gdb_output = [] + eu_unstrip_output = [] + + # execute gdb + process = subprocess.Popen( + ["gdb", "-c", path_to_core, path_to_executable], + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + + output = re.search(r"#0.*", str(process.communicate(input="bt"))) + try: + gdb_output = output.group().split("#") + except: + error("gdb output error") + + # execute eu-unstrip + process = subprocess.Popen( + ["eu-unstrip", "-n", "--core", path_to_core, "-e", path_to_executable], + stdout=subprocess.PIPE, + ) + output = process.communicate() + + eu_unstrip_output = str(output[0]).split("\n") + + for x in range(2, len(gdb_output)): + frame_list.append(get_frame(gdb_output[x])) + + for x in range(0, len(eu_unstrip_output) - 1): + image_list.append(get_image(eu_unstrip_output[x])) + + # build the json for sentry + data = { + "platform": "native", + "exception": { + "type": "Core", + "handled": "false", + "stacktrace": {"frames": [ob.to_json() for ob in frame_list]}, + }, + "debug_meta": {"images": [ob.to_json() for ob in image_list]}, + } + + if sentry_dsn is None: + sentry_sdk.init( + sentry_dsn + # "http://a707e782690f46ebb752810d1a08406a@host.docker.internal:8000/4" + ) + else: + sentry_sdk.init() + sentry_sdk.capture_event(data) + print("Core dump sent to sentry") + + +if __name__ == "__main__": + main()