From 75ba3d012c522b533aa1045cbcb843175f3ca400 Mon Sep 17 00:00:00 2001 From: drindhauser <57894940+drindhauser@users.noreply.github.com> Date: Fri, 28 Feb 2020 12:34:01 +0100 Subject: [PATCH] Bugfixes --- coredump_uploader/__init__.py | 125 ++++++++++++++++++++++------------ tests/test_basic.py | 48 ++++++++++++- 2 files changed, 125 insertions(+), 48 deletions(-) diff --git a/coredump_uploader/__init__.py b/coredump_uploader/__init__.py index 7e86af3..6a795a9 100644 --- a/coredump_uploader/__init__.py +++ b/coredump_uploader/__init__.py @@ -42,6 +42,7 @@ class Image: debug_id="", code_id="", code_file="", + arch="", ): self.type = type self.image_addr = image_addr @@ -49,6 +50,7 @@ class Image: self.debug_id = debug_id self.code_id = code_id self.code_file = code_file + self.arch = arch def to_json(self): return self.__dict__ @@ -235,7 +237,7 @@ def get_image(temp): ) -def get_threads(gdb_output, all_threads): +def get_threads(gdb_output): """Returns a list with all threads and backtraces""" thread_list = [] counter_threads = 0 @@ -282,13 +284,30 @@ def get_threads(gdb_output, all_threads): counter_threads += 1 thread_list.reverse() - if not all_threads and len(thread_list) > 1: - del thread_list[1:] - if all_threads: - print("Threads found: " + str(counter_threads)) + print("Threads found: " + str(counter_threads)) return thread_list, exit_signal, stacktrace_temp, crashed_thread_id +def get_stacktrace(gdb_output): + """Returns the stacktrace and the exit signal """ + if not "#0" in gdb_output: + error("gdb output error") + stacktrace = Stacktrace() + # Get the exit Signal from the gdb-output + exit_signal = re.search(_exit_signal_re, gdb_output) + if exit_signal: + exit_signal = exit_signal.group("type") + else: + exit_signal = "Core" + # Searches for frames in the GDB-Output + for match in re.finditer(_frame_re, gdb_output): + frame = get_frame(match) + if frame is not None: + stacktrace.append_frame(frame) + stacktrace.reverse_list() + return stacktrace, exit_signal + + def error(message): print("error: {}".format(message)) sys.exit(1) @@ -314,7 +333,7 @@ def signal_name_to_signal_number(signal_name): exit_signal_number = subprocess.check_output(["kill", temp]) except AttributeError: exit_signal_number = None - + return exit_signal_number @@ -351,26 +370,6 @@ class CoredumpUploader(object): self.elfutils_path = elfutils_path self.all_threads = all_threads - def get_stacktrace(self, gdb_output): - """Returns the stacktrace and the exit signal """ - if not "#0" in gdb_output: - error("gdb output error") - stacktrace = Stacktrace() - # Get the exit Signal from the gdb-output - exit_signal = re.search(_exit_signal_re, gdb_output) - if exit_signal: - exit_signal = exit_signal.group("type") - else: - exit_signal = "Core" - # Searches for frames in the GDB-Output - for match in re.finditer(_frame_re, gdb_output): - frame = get_frame(match) - if frame is not None: - stacktrace.append_frame(frame) - stacktrace.reverse_list() - - return stacktrace, exit_signal - def execute_gdb(self, path_to_core, gdb_command): """creates a subprocess for gdb and returns the output from gdb""" @@ -413,13 +412,13 @@ class CoredumpUploader(object): return output.decode("utf-8") def get_registers(self, path_to_core, stacktrace): - """Returns the stacktrace with the registers, the signal and the message.""" + """Returns the stacktrace with the registers, the gdb-version and the message.""" gdb_output = self.execute_gdb(path_to_core, "info registers") gdb_version = re.match(r"GNU gdb \(.*?\) (?P.*)", gdb_output) if gdb_version: gdb_version = gdb_version.group("gdb_version") - message = re.search(r"(?PCore was generated .*\n.*) ", gdb_output) + message = re.search(r"(?PCore was generated .*\n.*)", gdb_output) if message: message = message.group("message") @@ -440,10 +439,16 @@ class CoredumpUploader(object): if os.path.isfile(path_to_core) is not True: error("Wrong path to coredump") - gdb_output = self.execute_gdb(path_to_core, "thread apply all bt") - (thread_list, exit_signal, stacktrace, crashed_thread_id,) = get_threads( - gdb_output, self.all_threads - ) + if self.all_threads: + gdb_output = self.execute_gdb(path_to_core, "thread apply all bt") + (thread_list, exit_signal, stacktrace, crashed_thread_id,) = get_threads( + gdb_output + ) + else: + gdb_output = self.execute_gdb(path_to_core, "bt") + stacktrace, exit_signal = get_stacktrace(gdb_output) + thread_list = None + crashed_thread_id = None # gets the registers, the gdb-version and the message stacktrace, gdb_version, message = self.get_registers(path_to_core, stacktrace) @@ -463,13 +468,6 @@ class CoredumpUploader(object): # Get signal Number from signal name exit_signal_number = signal_name_to_signal_number(exit_signal) - # Make the image_list and thread_list to json - for i, image in enumerate(image_list): - try: - image_list[i] = image.to_json() - except: - continue - # Get elfutils version process = subprocess.Popen( [self.elfutils_path, "--version"], @@ -490,8 +488,8 @@ class CoredumpUploader(object): ["uname", "-s", "-r"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, ) os_context, err = process.communicate() + os_context = re.search(r"(?P.*?) (?P.*)", os_context) if os_context: - os_context = re.search(r"(?P.*?) (?P.*)", os_context) os_name = os_context.group("name") os_version = os_context.group("version") else: @@ -502,9 +500,44 @@ class CoredumpUploader(object): ) os_raw_context, err = process.communicate() + # Get App Contex + process = subprocess.Popen( + ["file", path_to_core], stdout=subprocess.PIPE, stdin=subprocess.PIPE, + ) + app_context, err = process.communicate() + app_context = re.search( + r"from '.*?( (?P.*))?', .* execfn: '.*\/(?P.*?)', platform: '(?P.*?)'", + app_context, + ) + if app_context: + args = app_context.group("args") + app_name = app_context.group("app_name") + arch = app_context.group("arch") + # Make a json from the Thread_list - for i, thread in enumerate(thread_list): - thread_list[i] = thread.to_json() + if thread_list: + for i, thread in enumerate(thread_list): + thread_list[i] = thread.to_json() + + # Make the image_list to json + for i, image in enumerate(image_list): + try: + if arch: + image_list[i].arch = arch + image_list[i] = image.to_json() + except: + continue + + # Get value, exception from message + message = re.search( + r"(?P.*)\n(?P.*?, (?P.*?)\.)", message + ) + if message: + value_exception = message.group("value") + type_exception = message.group("type") + message = message.group("message") + if type_exception is None: + type_exception = exit_signal # Build the json for sentry sentry_sdk.integrations.modules.ModulesIntegration = None @@ -516,9 +549,10 @@ class CoredumpUploader(object): "event_id": event_id, "timestamp": timestamp, "platform": "native", + "message": {"message": message,}, "exception": { - "value": message, - "type": exit_signal, + "value": value_exception, + "type": type_exception, "thread_id": crashed_thread_id, "mechanism": { "type": "coredump", @@ -547,6 +581,7 @@ class CoredumpUploader(object): "raw_description": os_raw_context, }, "runtime": None, + "app": {"app_name": app_name, "argv": args,}, }, "debug_meta": {"images": image_list}, "threads": {"values": thread_list}, @@ -607,7 +642,7 @@ def watch(context, watch_dir): observer.schedule(handler, watch_dir, recursive=False) observer.start() - print("Watchdog started, looking for new coredumps in dir: %s" % watch_dir) + print("Watchdog started, looking for new coredumps in : %s" % watch_dir) print("Press ctrl+c to stop\n") try: diff --git a/tests/test_basic.py b/tests/test_basic.py index 2770f5a..6b919b7 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -12,6 +12,7 @@ from coredump_uploader import Thread from coredump_uploader import Stacktrace from coredump_uploader import get_threads from coredump_uploader import signal_name_to_signal_number +from coredump_uploader import get_stacktrace def test_code_id_to_debug_id(): @@ -168,6 +169,7 @@ def test_image_to_json(): "debug_id": None, "code_id": "b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0", "code_file": "/lib/x86_64-linux-gnu/libc.so.6", + "arch": "", } @@ -246,9 +248,7 @@ Thread 3 (Thread 0x5846 (LWP 40)): ], ) def test_get_threads(gdb_output): - thread_list, exit_signal, stacktrace, crashed_thread_id = get_threads( - gdb_output, True - ) + thread_list, exit_signal, stacktrace, crashed_thread_id = get_threads(gdb_output) assert exit_signal == "SIGSEGV" assert thread_list[2].to_json() == { "id": "1", @@ -326,3 +326,45 @@ def test_get_threads(gdb_output): "registers": {}, } assert crashed_thread_id == "1" + + +@pytest.mark.parametrize( + "gdb_output", + [ + """ +For help, type "help". +Type "apropos word" to search for commands related to "word"... +Reading symbols from a.out...done. +[New LWP 1335] +Core was generated by `./a.out'. +Program terminated with signal SIGSEGV, Segmentation fault. +#0 0x000056416d5c760a in crashing_function () at ./test.c:3 +3 *bad_pointer = 1; +(gdb) bt +#0 0x000056416d5c760a in crashing_function () at ./test.c:3 +#1 0x000056416d5c761c in main () at ./test.c:7 +(gdb) quit""" + ], +) +def test_get_stacktrace(gdb_output): + stacktrace, exit_signal = get_stacktrace(gdb_output) + assert exit_signal == "SIGSEGV" + assert stacktrace.to_json() == { + "frames": [ + { + "filename": "./test.c", + "function": "crashing_function", + "instruction_addr": "0x000056416d5c760a", + "lineno": 3, + "package": None, + }, + { + "filename": "./test.c", + "function": "crashing_function", + "instruction_addr": "0x000056416d5c760a", + "lineno": 3, + "package": None, + }, + ], + "registers": {}, + }