class StateOutput(object): parser = Parser() @staticmethod def parse(data): assert data[0] == Communicator.EXEC_ASYNC_START data = data[1:] state = OutputParser.get_name_until_param(data) if state == "running": return StateOutput(ProcessState.Running) elif state == "stopped": data = data[len(state) + 1:] data = StateOutput.parser.parse(data) state = ProcessState.Stopped exit_code = None reason = "" if "reason" in data: reason = data["reason"] if "exited" in reason: state = ProcessState.Exited if "exit-code" in data: exit_code = int(data["exit-code"]) elif "normally" in reason: exit_code = 0 return StateOutput(state, reason, exit_code) else: raise Exception("No state found for data: {0}".format(data)) def __init__(self, state, reason=None, exit_code=None): """ @type state: enums.ProcessState @type reason: enums.StopReason @type exit_code: int """ self.state = state self.reason = reason self.exit_code = exit_code
def __init__(self, debugger): """ @type debugger: Debugger """ super(VariableManager, self).__init__(debugger) self.parser = Parser()
class VariableManager(debugger_api.VariableManager): RECURSION_LIMIT = 3 """ Handles retrieval and updating of variables and raw memory of the debugged process. """ def __init__(self, debugger): """ @type debugger: Debugger """ super(VariableManager, self).__init__(debugger) self.parser = Parser() def get_type(self, expression, level=0): """ Returns type for the given expression. @type expression: str @type level: int @rtype: debugee.Type """ if level > VariableManager.RECURSION_LIMIT: return None output = self.debugger.communicator.send("ptype {0}". format(expression)) short_output = self.debugger.communicator.send("whatis {0}". format(expression)) if output and short_output: try: type = self.parser.parse_variable_type(output.cli_data[0]) type_name = self.parser.parse_variable_type( short_output.cli_data[0]) except: Logger.debug(traceback.format_exc()) return None basic_type_category = BasicTypeCategory.Invalid type_category = TypeCategory.Class modificators = [] try: while type.startswith("volatile") or type.startswith("const"): modificator = type[:type.find(" ")] type = type[len(modificator) + 1:] modificators.append(modificator) if type_name.startswith(modificator): type_name = type_name[len(modificator) + 1:] except: Logger.debug(traceback.format_exc()) return None if type in basic_type_map: basic_type_category = basic_type_map[type] type_category = TypeCategory.Builtin else: if type_name.startswith("std::vector"): type_category = TypeCategory.Vector elif type_name.startswith("std::string"): type_category = TypeCategory.String elif type_name.endswith("*"): type_category = TypeCategory.Pointer elif type_name.endswith("&"): type_category = TypeCategory.Reference elif type_name.endswith("]"): type_category = TypeCategory.Array elif type_name.endswith(")"): type_category = TypeCategory.Function elif type.startswith("struct"): type_category = TypeCategory.Struct elif type.startswith("class"): type_category = TypeCategory.Class elif type.startswith("union"): type_category = TypeCategory.Union elif type.startswith("enum"): type_category = TypeCategory.Enumeration size = None size_output = self.debugger.communicator.send("p sizeof({0})". format(type_name)) if size_output: try: size = int(self.parser.parse_print_expression( size_output.cli_data[0])) except: Logger.debug(traceback.format_exc()) return None args = [type_name, type_category, basic_type_category, size, tuple(modificators)] try: if type_category == TypeCategory.Array: right_bracket_end = type_name.rfind("]") right_bracket_start = type_name.rfind("[") count = int(type_name[ right_bracket_start + 1:right_bracket_end]) child_type = self.get_type("{}[0]".format(expression), level + 1) type = ArrayType(count, child_type, *args) elif type_category == TypeCategory.Vector: child_type = self.debugger.communicator.send( "python print(gdb.lookup_type(\"{}\")" ".template_argument(0))".format(type_name)) child_type = self.get_type(" ".join(child_type.cli_data), level + 1) type = ArrayType(0, child_type, *args) else: type = Type(*args) except: Logger.debug(traceback.format_exc()) return None return type else: return None def get_variable(self, expression, level=0): """ Returns a variable for the given expression- @type expression: str @type level: int @rtype: debugee.Variable """ if level > VariableManager.RECURSION_LIMIT: return None type = self.get_type(expression) output = self.debugger.communicator.send("p {0}".format(expression)) if output and type: try: data = self.parser.parse_print_expression(output.cli_data) except: Logger.debug(traceback.format_exc()) return None address = None address_output = self.debugger.communicator.send( "p &{0}".format(expression)) try: if address_output: address = self._parse_address(address_output.cli_data) except: Logger.debug(traceback.format_exc()) name = self._get_name(expression) value = None variable = None children = [] try: if type.type_category == TypeCategory.Builtin: value = data variable = Variable(address, name, value, type, expression) elif type.type_category == TypeCategory.Pointer: value = data[data.rfind(" ") + 1:].lower() target_type = self.get_type("*{0}".format(expression)) if (target_type and BasicTypeCategory.is_char( target_type.basic_type_category)): type.type_category = TypeCategory.CString value = value[1:-1] # strip quotes variable = PointerVariable(target_type, address, name, value, type, expression) elif type.type_category == TypeCategory.Reference: value = data[data.find("@") + 1:data.find(":")] address = self.debugger.communicator.send( "p &(&{0})".format(expression)) if address: address = self._parse_address(address.cli_data) else: address = "0x0" target_type = self.get_type("*{0}".format(expression)) variable = PointerVariable(target_type, address, name, value, type, expression) elif type.type_category == TypeCategory.Function: # skip function pointer type if data.startswith("({}) ".format(type.name)): data = data[(3 + len(type.name)):] variable = Variable(address, name, data, type, expression) elif type.type_category == TypeCategory.String: value = data.strip("\"") variable = Variable(address, name, value, type, expression) elif type.type_category in (TypeCategory.Class, TypeCategory.Struct, TypeCategory.Union): result = self.debugger.communicator.send( "python print([field.name for field in " "gdb.lookup_type(\"{0}\").fields()])".format(type.name) ) if result: members = self.parser.parse_struct_member_names( result.cli_data[0]) for member in members: child = self.get_variable("({0}).{1}".format( expression, member), level + 1) if child: children.append(child) variable = Variable(address, name, value, type, expression) elif type.type_category == TypeCategory.Vector: length = self.get_variable( "({0}._M_impl._M_finish - {0}._M_impl._M_start)" .format(expression), level + 1) if length: length = int(length.value) data_address = self.debugger.communicator.send( "p {}._M_impl._M_start".format(expression)) data_address = " ".join(data_address.cli_data).rstrip() data_address = data_address[data_address.rfind(" ") + 1:] variable = VectorVariable(length, data_address, address, name, value, type, expression) elif type.type_category == TypeCategory.Array: length = type.count data_address = self.get_variable( "&({}[0])".format(expression), level + 1) if data_address: data_address = data_address.value else: data_address = "" variable = VectorVariable(length, data_address, address, name, value, type, expression) elif type.type_category == TypeCategory.Enumeration: variable = Variable(address, name, data, type, expression) else: return None except: Logger.debug(traceback.format_exc()) return None if variable: variable.on_value_changed.subscribe(self.update_variable) for child in children: if child: variable.add_child(child) return variable else: return None def update_variable(self, variable): """ Updates the variable's value in the debugged process. @type variable: debugee.Variable """ format = "set variable *{0} = {1}" value = variable.value try: if variable.type.type_category == TypeCategory.String: format = "call static_cast<std::string*>({0})->assign(\"{1}\")" elif BasicTypeCategory.is_char(variable.type.basic_type_category): char_value = variable.value if len(char_value) == 1 and not char_value[0].isdigit(): value = "'{}'".format(char_value) except: Logger.debug(traceback.format_exc()) return False result = self.debugger.communicator.send(format.format( variable.address, value)) return result.is_success() def get_memory(self, address, count): """ Returns count bytes from the given address. @type address: str @type count: int @rtype: list of int """ command = "x/{0}xb {1}".format(count, address) output = self.debugger.communicator.send(command) bytes = [] try: for line in output.cli_data: start = line.find(":") line = line[start + 1:] for num in line.split("\\t"): if num: bytes.append(int(num, 16)) except: Logger.debug(traceback.format_exc()) return bytes def get_registers(self): """ Returns the register values as a list of tuples with name and value of the given register. @rtype: list of register.Register """ try: register_names = self.debugger.communicator.send( "-data-list-register-names") if not register_names: return [] register_names = self.parser.parse( register_names.data)["register-names"] register_values = self.debugger.communicator.send( "-data-list-register-values --skip-unavailable x") if not register_values: return [] registers = [] register_values = self.parser.parse( register_values.data)["register-values"] for reg in register_values: number = int(reg["number"]) if (number < len(register_names) and len(register_names[number]) > 0): registers.append(Register(str(register_names[number]), str(reg["value"]))) return registers except: Logger.debug(traceback.format_exc()) return [] def get_vector_items(self, vector): """ @type vector: debugger.debugee.VectorVariable @rtype: list of debugger.debugee.Variable """ items = [] for i in xrange(vector.start, vector.start + vector.count): expression = vector.path if vector.type.type_category == TypeCategory.Array: expression += "[{}]".format(i) elif vector.type.type_category == TypeCategory.Vector: expression = "*({}._M_impl._M_start + {})".format( expression, i) var = self.get_variable(expression) if var: items.append(var) vector.children = items return items def _get_name(self, expression): """ Returns name from the given expression. @type expression: str @rtype: str """ match = re.search("([a-zA-Z_$]+)$", expression) if match: return match.group(0) else: return expression def _parse_address(self, expression): """ @type expression: str @rtype: str | None """ try: address = self.parser.parse_print_expression(expression) if len(address) > 0 and address[0] == "(": return address[address.rfind(" ") + 1:] elif address[:2] == "0x": return address[:address.find(" ")] except: Logger.debug(traceback.format_exc()) return None
def parser(): return Parser()
def __init__(self, debugger): super(BreakpointManager, self).__init__(debugger) self.parser = Parser()
class BreakpointManager(debugger_api.BreakpointManager): def __init__(self, debugger): super(BreakpointManager, self).__init__(debugger) self.parser = Parser() def add_breakpoint(self, location, line): """ Adds a breakpoint, if there is not a breakpoint with the same location and line already. @type location: str @type line: int @rtype: boolean """ self.debugger.require_state(DebuggerState.BinaryLoaded) if self.find_breakpoint(location, line) is not None: return False if line is not None: location += ":" + str(line) success = self.debugger.communicator.send( "-break-insert {0}".format(location)).is_success() if success: self.on_breakpoint_changed.notify(self.find_breakpoint(location, line)) return success def toggle_breakpoint(self, location, line): """ Toggles a breakpoint on the given location and line. @type location: str @type line: int @rtype: boolean """ bp = self.find_breakpoint(location, line) if bp: return self.remove_breakpoint(location, line) else: return self.add_breakpoint(location, line) def get_breakpoints(self): """ @rtype: list of debugger.Breakpoint """ bps = self.debugger.communicator.send("-break-list") if bps: try: return self.parser.parse_breakpoints(bps.data) except: return [] else: return [] def find_breakpoint(self, location, line): """ @type location: str @type line: int @rtype: debugger.Breakpoint | None """ location = os.path.abspath(location) for bp in self.get_breakpoints(): if bp.location == location and bp.line == line: return bp return None def remove_breakpoint(self, location, line): """ @type location: str @type line: int @rtype: boolean """ self.debugger.require_state(DebuggerState.BinaryLoaded) bp = self.find_breakpoint(location, line) if bp: success = self.debugger.communicator.send( "-break-delete {0}".format(bp.number)).is_success() if success: self.on_breakpoint_changed.notify(bp) return success else: return False
class BreakpointManager(debugger_api.BreakpointManager): def __init__(self, debugger): super(BreakpointManager, self).__init__(debugger) self.parser = Parser() def add_breakpoint(self, location, line): """ Adds a breakpoint, if there is not a breakpoint with the same location and line already. @type location: str @type line: int @rtype: boolean """ self.debugger.require_state(DebuggerState.BinaryLoaded) if self.find_breakpoint(location, line) is not None: return False if line is not None: location += ":" + str(line) success = self.debugger.communicator.send( "-break-insert {0}".format(location)).is_success() if success: self.on_breakpoint_changed.notify( self.find_breakpoint(location, line)) return success def toggle_breakpoint(self, location, line): """ Toggles a breakpoint on the given location and line. @type location: str @type line: int @rtype: boolean """ bp = self.find_breakpoint(location, line) if bp: return self.remove_breakpoint(location, line) else: return self.add_breakpoint(location, line) def get_breakpoints(self): """ @rtype: list of debugger.Breakpoint """ bps = self.debugger.communicator.send("-break-list") if bps: try: return self.parser.parse_breakpoints(bps.data) except: return [] else: return [] def find_breakpoint(self, location, line): """ @type location: str @type line: int @rtype: debugger.Breakpoint | None """ location = os.path.abspath(location) for bp in self.get_breakpoints(): if bp.location == location and bp.line == line: return bp return None def remove_breakpoint(self, location, line): """ @type location: str @type line: int @rtype: boolean """ self.debugger.require_state(DebuggerState.BinaryLoaded) bp = self.find_breakpoint(location, line) if bp: success = self.debugger.communicator.send( "-break-delete {0}".format(bp.number)).is_success() if success: self.on_breakpoint_changed.notify(bp) return success else: return False
def __init__(self, debugger): """ @type debugger: debugger.Debugger """ super(ThreadManager, self).__init__(debugger) self.parser = Parser()
class ThreadManager(debugger_api.ThreadManager): def __init__(self, debugger): """ @type debugger: debugger.Debugger """ super(ThreadManager, self).__init__(debugger) self.parser = Parser() def get_current_thread(self): """ @rtype: debugee.Thread """ thread_info = self.get_thread_info() if thread_info: return thread_info.selected_thread else: return None def get_thread_info(self): """ Returns (active_thread_id, all_threads). @rtype: debugee.ThreadInfo | None """ output = self.debugger.communicator.send("-thread-info") if output: try: return self.parser.parse_thread_info(output.data) except: Logger.debug(traceback.format_exc()) return None def set_thread_by_index(self, thread_id): """ @type thread_id: int @rtype: bool """ result = self.debugger.communicator.send( "-thread-select {0}".format(thread_id)).is_success() if result: self.debugger.on_thread_changed.notify( self.get_thread_info().selected_thread) self.debugger.on_frame_changed.notify(self.get_current_frame()) util.Logger.debug( "Changed to thread with id {0}".format(thread_id)) return True else: return False def get_current_frame(self, with_variables=False): """ Returns the current stack frame, optionally with it's parameters. @type with_variables: bool @rtype: debugee.Frame | None """ output = self.debugger.communicator.send("-stack-info-frame") if output: try: frame = self.parser.parse_stack_frame(output.data) if with_variables: variables_info = self.parser.parse_frame_variables( self.debugger.communicator.send( "-stack-list-variables --skip-unavailable 0").data) for variable in variables_info: try: variable = self.debugger.variable_manager.\ get_variable(variable["name"]) if variable: frame.variables.append(variable) except: Logger.debug( "Coult not load variable {}".format(variable)) return frame except: Logger.debug(traceback.format_exc()) return None def get_frames(self): """ @rtype: list of debugee.Frame """ output = self.debugger.communicator.send("-stack-list-frames") if output: try: return self.parser.parse_stack_frames(output.data) except: Logger.debug(traceback.format_exc()) return [] def get_frames_with_variables(self): """ Returns all stack frames with all their local variables and arguments. @rtype: list of debugger.debugee.Frame """ current_frame = self.get_current_frame(False) if not current_frame: return [] frames = [] try: for i, fr in enumerate(self.get_frames()): self.change_frame(i, False) frames.append(self.get_current_frame(True)) except: Logger.debug(traceback.format_exc()) finally: self.change_frame(current_frame.level, False) return frames def change_frame(self, frame_index, notify=True): """ @type frame_index: int @type notify: bool @rtype: bool """ if frame_index >= len(self.get_frames()): return False result = self.debugger.communicator.send( "-stack-select-frame {0}".format(frame_index)).is_success() if result: if notify: self.debugger.on_frame_changed.notify(self.get_current_frame()) util.Logger.debug( "Changed to frame with id {0}".format(frame_index)) return True else: return False
class ThreadManager(debugger_api.ThreadManager): def __init__(self, debugger): """ @type debugger: debugger.Debugger """ super(ThreadManager, self).__init__(debugger) self.parser = Parser() def get_current_thread(self): """ @rtype: debugee.Thread """ thread_info = self.get_thread_info() if thread_info: return thread_info.selected_thread else: return None def get_thread_info(self): """ Returns (active_thread_id, all_threads). @rtype: debugee.ThreadInfo | None """ output = self.debugger.communicator.send("-thread-info") if output: try: return self.parser.parse_thread_info(output.data) except: Logger.debug(traceback.format_exc()) return None def set_thread_by_index(self, thread_id): """ @type thread_id: int @rtype: bool """ result = self.debugger.communicator.send( "-thread-select {0}".format(thread_id)).is_success() if result: self.debugger.on_thread_changed.notify( self.get_thread_info().selected_thread) self.debugger.on_frame_changed.notify(self.get_current_frame()) util.Logger.debug("Changed to thread with id {0}".format( thread_id)) return True else: return False def get_current_frame(self, with_variables=False): """ Returns the current stack frame, optionally with it's parameters. @type with_variables: bool @rtype: debugee.Frame | None """ output = self.debugger.communicator.send("-stack-info-frame") if output: try: frame = self.parser.parse_stack_frame(output.data) if with_variables: variables_info = self.parser.parse_frame_variables( self.debugger.communicator.send( "-stack-list-variables --skip-unavailable 0").data ) for variable in variables_info: try: variable = self.debugger.variable_manager.\ get_variable(variable["name"]) if variable: frame.variables.append(variable) except: Logger.debug("Coult not load variable {}" .format(variable)) return frame except: Logger.debug(traceback.format_exc()) return None def get_frames(self): """ @rtype: list of debugee.Frame """ output = self.debugger.communicator.send("-stack-list-frames") if output: try: return self.parser.parse_stack_frames(output.data) except: Logger.debug(traceback.format_exc()) return [] def get_frames_with_variables(self): """ Returns all stack frames with all their local variables and arguments. @rtype: list of debugger.debugee.Frame """ current_frame = self.get_current_frame(False) if not current_frame: return [] frames = [] try: for i, fr in enumerate(self.get_frames()): self.change_frame(i, False) frames.append(self.get_current_frame(True)) except: Logger.debug(traceback.format_exc()) finally: self.change_frame(current_frame.level, False) return frames def change_frame(self, frame_index, notify=True): """ @type frame_index: int @type notify: bool @rtype: bool """ if frame_index >= len(self.get_frames()): return False result = self.debugger.communicator.send( "-stack-select-frame {0}".format(frame_index)).is_success() if result: if notify: self.debugger.on_frame_changed.notify(self.get_current_frame()) util.Logger.debug("Changed to frame with id {0}".format( frame_index)) return True else: return False
class FileManager(debugger_api.FileManager): def __init__(self, debugger): """ @type debugger: debugger.Debugger """ super(FileManager, self).__init__(debugger) self.parser = Parser() def _get_current_line(self): output = self.debugger.communicator.send("info line") if output: try: cli_data = output.cli_data[0][5:] match = re.match("(\d+)", cli_data) return int(match.group(0)) except: Logger.debug(traceback.format_exc()) return None def _get_current_file(self): output = self.debugger.communicator.send("info source") if output: try: cli_data = output.cli_data[2] return cli_data[11:] except: Logger.debug(traceback.format_exc()) return None def _get_single_capture(self, pattern, input): match = re.search(pattern, input) if not match: return None else: return match.group(1) def get_main_source_file(self): output = self.debugger.communicator.send("info sources") if output: try: files = output.cli_data[1] return files.split(",")[0] except: Logger.debug(traceback.format_exc()) return None def get_current_location(self): """ Returns the current file and line of the debugged process. @rtype: tuple of basestring, int | None """ frame = self.debugger.thread_manager.get_current_frame(False) if not frame: return None line = frame.line location = frame.file Logger.debug("Getting current location: ({0}, {1})".format( location, line)) return (location, line) def get_line_address(self, filename, line): """ Returns the starting address and ending address in hexadecimal format of code at the specified line in the given file. Returns None if no code is at the given location. @type filename: str @type line: int @rtype: tuple of int | None """ command = "info line {0}:{1}".format(filename, line) result = self.debugger.communicator.send(command) if result: data = result.cli_data[0] if "address" not in data or "contains no code" in data: return None start_address = self._get_single_capture( "starts at address ([^ ]*)", data) end_address = self._get_single_capture("ends at ([^ ]*)", data) if start_address and end_address: return (start_address, end_address) return None def disassemble(self, filename, line): """ Returns disassembled code for the given location. Returns None if no code was found, @type filename: str @type line: int @rtype: str | None """ command = "-data-disassemble -f {0} -l {1} -n 10 -- 1".format( filename, line) result = self.debugger.communicator.send(command) if result: try: disassembled = self.parser.parse_disassembly(result.data) if disassembled: return disassembled except: Logger.debug(traceback.format_exc()) return None def disassemble_raw(self, filename, line): """ Disassembles the given line in a raw form (returns a string with the line and all assembly instructions for it). @type filename: str @type line: int @rtype: str | None """ address = self.get_line_address(filename, line) if not address: return None command = "disas /m {0}, {1}".format(address[0], address[1]) result = self.debugger.communicator.send(command) if result: try: return "\n".join([ row.replace("\\t", "\t") for row in result.cli_data[1:-1] ]) except: Logger.debug(traceback.format_exc()) return None