def ensure_lldb_is_running(cls, w=None): """Returns True if lldb is running (we don't care if we started it or not). Returns False on error""" # Ensure we reflect any changes to saved settings (including project settings) # reload_settings() if not w and window_ref(): w = window_ref() else: # We're redefining the default window. set_window_ref(w) if driver_instance() is None: sm = SettingsManager.getSM() clear_view_on_startup = sm.get_default('i/o.view.clear_on_startup', True) if clear_view_on_startup: LLDBLayoutManager.clear_view(lldb_out_view()) if not cls.start_debugging(w): return False set_ui_updater(LLDBUIUpdater()) g = cls.lldb_greeting() if lldb_out_view().size() > 0: g = '\n\n' + cls.lldb_greeting() lldb_view_write(g) lldb_view_write('cwd: ' + os.getcwd() + '\n') w.set_view_index(lldb_out_view(), 1, 0) cls.debug_prologue(driver_instance()) return True return True
def initialize_plugin(cls): global _initialized if _initialized: return thread_created('<' + threading.current_thread().name + '>') debug(debugAny, 'Loading LLDB Sublime Text 2 plugin') debug(debugAny, 'python version: %s' % (sys.version_info,)) debug(debugAny, 'cwd: %s' % os.getcwd()) sm = SettingsManager.getSM() use_bundled_debugserver = sm.get_default('debugserver.use_bundled', False) debugserver_path = sm.get_default('debugerver.path', None) global _did_not_find_debugserver found = False if debugserver_path is not None: # TODO: Check that it is a file if os.access(debugserver_path, os.X_OK): os.environ['LLDB_DEBUGSERVER_PATH'] = debugserver_path found = True else: # FIXME: Warn the user that the debugserver isn't executable _did_not_find_debugserver = True elif not use_bundled_debugserver: cls.find_debugserver() if found: debug(debugPlugin, 'debugserver path: %s' % os.environ['LLDB_DEBUGSERVER_PATH']) _initialized = True
def set_lldb_window_layout(cls, window=window_ref()): sm = SettingsManager.getSM() lldb_window_layout = sm.get_default('layout', _default_lldb_window_layout) if lldb_out_view() != None and window.num_groups() != len( lldb_window_layout['cells']): window.run_command('set_layout', lldb_window_layout)
def run(self): self.setup() if LLDBPlugin.ensure_lldb_is_running(self.window): sublime.status_message('Debugging session started.') else: sublime.error_message('Couldn\'t get a debugging session.') return False LLDBLayoutManager.lldb_toggle_output_view(self.window, show=True) exe = LLDBPlugin.search_for_executable() sm = SettingsManager.getSM() arch = sm.get_default('arch', lldb.LLDB_ARCH_DEFAULT) if exe: args = map(str, sm.get_default('args', [])) debug( debugPlugin, 'Launching program: ' + exe + ' (' + arch + '), with args: ' + str(args)) t = driver_instance().debugger.CreateTargetWithFileAndArch( str(exe), str(arch)) driver_instance().debugger.SetSelectedTarget(t) sublime.status_message('Setting default breakpoints.') create_default_bps_for_target(t) sublime.status_message('Launching program (%s): %s %s' % (arch, exe, args)) if t.LaunchSimple(args, None, os.getcwd()): sublime.status_message('Program successfully launched.') else: sublime.error_message('Program failed to launch.')
def run(self): self.setup() if LLDBPlugin.ensure_lldb_is_running(self.window): sublime.status_message('Debugging session started.') else: sublime.error_message('Couldn\'t get a debugging session.') return False LLDBLayoutManager.lldb_toggle_output_view(self.window, show=True) exe = LLDBPlugin.search_for_executable() sm = SettingsManager.getSM() arch = sm.get_default('arch', lldb.LLDB_ARCH_DEFAULT) if exe: args = map(str, sm.get_default('args', [])) debug(debugPlugin, 'Launching program: ' + exe + ' (' + arch + '), with args: ' + str(args)) t = driver_instance().debugger.CreateTargetWithFileAndArch(str(exe), str(arch)) driver_instance().debugger.SetSelectedTarget(t) sublime.status_message('Setting default breakpoints.') create_default_bps_for_target(t) sublime.status_message('Launching program (%s): %s %s' % (arch, exe, args)) if t.LaunchSimple(args, None, os.getcwd()): sublime.status_message('Program successfully launched.') else: sublime.error_message('Program failed to launch.')
def initialize_plugin(cls): global _initialized if _initialized: return thread_created('<' + threading.current_thread().name + '>') debug(debugAny, 'Loading LLDB Sublime Text 2 plugin') debug(debugAny, 'python version: %s' % (sys.version_info, )) debug(debugAny, 'cwd: %s' % os.getcwd()) sm = SettingsManager.getSM() use_bundled_debugserver = sm.get_default('debugserver.use_bundled', False) debugserver_path = sm.get_default('debugerver.path', None) global _did_not_find_debugserver found = False if debugserver_path is not None: # TODO: Check that it is a file if os.access(debugserver_path, os.X_OK): os.environ['LLDB_DEBUGSERVER_PATH'] = debugserver_path found = True else: # FIXME: Warn the user that the debugserver isn't executable _did_not_find_debugserver = True elif not use_bundled_debugserver: cls.find_debugserver() if found: debug(debugPlugin, 'debugserver path: %s' % os.environ['LLDB_DEBUGSERVER_PATH']) _initialized = True
def run(self): self.setup() sm = SettingsManager.getSM() basic_layout = sm.get_default('layout.basic', _default_basic_window_layout) if LLDBLayoutManager.good_lldb_layout(window=self.window) and basic_layout != None: # restore backup_layout (groups and views) LLDBLayoutManager.lldb_toggle_output_view(self.window, hide=True) else: LLDBLayoutManager.lldb_toggle_output_view(self.window, show=True)
def run(self): self.setup() sm = SettingsManager.getSM() basic_layout = sm.get_default('layout.basic', _default_basic_window_layout) if LLDBLayoutManager.good_lldb_layout( window=self.window) and basic_layout != None: # restore backup_layout (groups and views) LLDBLayoutManager.lldb_toggle_output_view(self.window, hide=True) else: LLDBLayoutManager.lldb_toggle_output_view(self.window, show=True)
def debug_prologue(cls, driver): """ Prologue for the debugging session during the development of the plugin. Loads a simple program in the debugger and sets a breakpoint in main() """ sm = SettingsManager.getSM() prologue = sm.get_default('prologue', []) debug(debugPlugin, 'LLDB prologue: %s' % str(prologue)) for c in prologue: lldb_view_write(lldb_prompt() + c + '\n') driver.interpret_command(c)
def get_lldb_output_view(window, name=None): # Search for the lldb_view view first. if not name: sm = SettingsManager.getSM() name = sm.get_default('i/o.view.name', default_lldb_view_name) f = maybe_get_lldb_output_view(window, name) if f is None: f = window.new_file() f.set_name(name) f.set_scratch(True) f.set_read_only(True) # f.set_syntax_file('…') # lldb output syntax return f
def on_done(self, string): if self.__process: # Check if it's still valid addr = int(string, 0) error = lldb.SBError() sm = SettingsManager.getSM() view_mem_size = sm.get_default('view.memory.size', 512) view_mem_width = sm.get_default('view.memory.width', 32) view_mem_grouping = sm.get_default('view.memory.grouping', 8) content = self.__process.ReadMemory(addr, view_mem_size, error) if error.Fail(): sublime.error_message(error.GetCString()) return None # Use 'ascii' encoding as each byte of 'content' is within [0..255]. new_bytes = bytearray(content, 'latin1') result = generate_memory_view_for(addr, new_bytes, view_mem_width, view_mem_grouping) # Re-use a view, if we already have one. v = None name = self.__owner._view_memory_view_prefix + hex(addr) for _v in self.window.views(): if _v.name() == name: v = _v break if v is None: layout_group_source_file = sm.get_default( 'layout.group.source_file', 0) self.window.focus_group(layout_group_source_file) v = self.window.new_file() v.set_name(name) LLDBLayoutManager.clear_view(v) v.set_scratch(True) v.set_read_only(False) edit = v.begin_edit('lldb-view-memory-' + hex(addr)) v.insert(edit, 0, result) v.end_edit(edit) v.set_read_only(True)
def lldb_view_write(string): global __out_view, __window_ref if not (__out_view and __window_ref and __out_view.window()): sm = SettingsManager.getSM() name = sm.get_default('i/o.view.name', default_lldb_view_name) __out_view = get_lldb_output_view(__window_ref, name) if not __window_ref: # Bail out and just set the first window __window_ref = sublime.windows()[0] # __window_ref.set_view_index(__out_view, 1, 0) __out_view.set_read_only(False) edit = __out_view.begin_edit('lldb-panel-write') __out_view.insert(edit, __out_view.size(), string) __out_view.end_edit(edit) __out_view.set_read_only(True) __out_view.show(__out_view.size())
def create_default_bps_for_target(target): n = 0 sm = SettingsManager.getSM() bps = sm.get_default('breakpoints', []) for bp in bps: if not bp: continue if type(bp) is str or type(bp) is unicode: bp = str(bp) m = bp_re_file_line.match(bp) if m: target.BreakpointCreateByLocation(m.group(1), m.group(2)) ++n continue m = bp_re_address.match(bp) if m: target.BreakpointCreateByAddress(m.group(1)) ++n continue m = bp_re_name.match(bp) if m: target.BreakpointCreateByName(m.group(1)) ++n continue debug(debugPlugin, "couldn't tell where the bp spec '" + bp + "' should break.") # bp isn't an str. It should be a dict elif 'file' in bp and 'line' in bp: target.BreakpointCreateByLocation(str(bp['file']), bp['line']) ++n elif 'address' in bp: target.BreakpointCreateByAddress(bp['address']) ++n else: debug(debugPlugin, 'unrecognized breakpoint type: ' + str(bp))
def on_done(self, string): if self.__process: # Check if it's still valid addr = int(string, 0) error = lldb.SBError() sm = SettingsManager.getSM() view_mem_size = sm.get_default('view.memory.size', 512) view_mem_width = sm.get_default('view.memory.width', 32) view_mem_grouping = sm.get_default('view.memory.grouping', 8) content = self.__process.ReadMemory(addr, view_mem_size, error) if error.Fail(): sublime.error_message(error.GetCString()) return None # Use 'ascii' encoding as each byte of 'content' is within [0..255]. new_bytes = bytearray(content, 'latin1') result = generate_memory_view_for(addr, new_bytes, view_mem_width, view_mem_grouping) # Re-use a view, if we already have one. v = None name = self.__owner._view_memory_view_prefix + hex(addr) for _v in self.window.views(): if _v.name() == name: v = _v break if v is None: layout_group_source_file = sm.get_default('layout.group.source_file', 0) self.window.focus_group(layout_group_source_file) v = self.window.new_file() v.set_name(name) LLDBLayoutManager.clear_view(v) v.set_scratch(True) v.set_read_only(False) edit = v.begin_edit('lldb-view-memory-' + hex(addr)) v.insert(edit, 0, result) v.end_edit(edit) v.set_read_only(True)
def set_regular_window_layout(cls, window=window_ref()): sm = SettingsManager.getSM() basic_layout = sm.get_default('layout.basic', _default_basic_window_layout) window.run_command('set_layout', basic_layout)
def search_for_executable(cls): sm = SettingsManager.getSM() exe = sm.get_default('exe', None) return exe
def good_lldb_layout(cls, window=window_ref()): # if the user already has two groups, it's a good layout sm = SettingsManager.getSM() lldb_window_layout = sm.get_default('layout', _default_lldb_window_layout) return window.num_groups() == len(lldb_window_layout['cells'])
def on_done(self, string): if LLDBPlugin.ensure_lldb_is_running(self.window): sublime.status_message('Debugging session started.') else: sublime.error_message('Couldn\'t get a debugging session.') return False driver = driver_instance() if driver: # Check if we have a previously running program target = driver.debugger.GetSelectedTarget() if not target: target = driver.debugger.CreateTarget('') if not target: sublime.error_message('Error attaching to process') driver.debugger.SetSelectedTarget(target) old_exec_module = target.GetExecutable() old_triple = target.GetTriple() # attach_info = lldb.SBAttachInfo() # If the user didn't specify anything, attach to the program from # the current target, if it exists # if string is '': # if old_exec_module: # attach_info.SetExecutable(old_exec_module) # else: # # Bail out # sublime.error_message('No process name/ID specified and no current target.') # return # else: error = lldb.SBError() sm = SettingsManager.getSM() wait_for_launch = sm.get_default('attach.wait_for_launch', False) try: pid = int(string) # attach_info.SetProcessID(pid) debug(debugPlugin, 'Attaching to pid: %d' % pid) sublime.status_message('Attaching to pid: %d' % pid) process = target.AttachToProcessWithID(lldb.SBListener(), pid, error) except ValueError: # We have a process name, not a pid. # pid = lldb.LLDB_INVALID_PROCESS_ID # attach_info.SetExecutable(str(string)) name = str(string) if string != '' else old_exec_module debug(debugPlugin, 'Attaching to process: %s (wait=%s)' % (name, str(wait_for_launch))) sublime.status_message('Attaching to process: %s (wait=%s)' % (name, wait_for_launch)) process = target.AttachToProcessWithName(lldb.SBListener(), name, wait_for_launch, error) # attach_info.SetWaitForLaunch(wait_for_launch) # error = lldb.SBError() # debug(debugPlugin, attach_info) # process = target.Attach(attach_info, error) debug(debugPlugin, process) if error.Fail(): sublime.error_message("Attach failed: %s" % error.GetCString()) new_exec_module = target.GetExecutable() if new_exec_module != old_exec_module: debug(debugPlugin, 'Executable module changed from "%s" to "%s".' % \ (old_exec_module, new_exec_module)) new_triple = target.GetTriple() if new_triple != old_triple: debug(debugPlugin, 'Target triple changed from "%s" to "%s".' % (old_triple, new_triple)) # How can we setup the default breakpoints? # We *could* start a new thread with a listener, just for that... else: sublime.error_message('Couldn\'t get a debugging session.')
def on_done(self, string): if LLDBPlugin.ensure_lldb_is_running(self.window): sublime.status_message('Debugging session started.') else: sublime.error_message('Couldn\'t get a debugging session.') return False driver = driver_instance() if driver: # Check if we have a previously running program target = driver.debugger.GetSelectedTarget() if not target: target = driver.debugger.CreateTarget('') if not target: sublime.error_message('Error attaching to process') driver.debugger.SetSelectedTarget(target) old_exec_module = target.GetExecutable() old_triple = target.GetTriple() # attach_info = lldb.SBAttachInfo() # If the user didn't specify anything, attach to the program from # the current target, if it exists # if string is '': # if old_exec_module: # attach_info.SetExecutable(old_exec_module) # else: # # Bail out # sublime.error_message('No process name/ID specified and no current target.') # return # else: error = lldb.SBError() sm = SettingsManager.getSM() wait_for_launch = sm.get_default('attach.wait_for_launch', False) try: pid = int(string) # attach_info.SetProcessID(pid) debug(debugPlugin, 'Attaching to pid: %d' % pid) sublime.status_message('Attaching to pid: %d' % pid) process = target.AttachToProcessWithID( lldb.SBListener(), pid, error) except ValueError: # We have a process name, not a pid. # pid = lldb.LLDB_INVALID_PROCESS_ID # attach_info.SetExecutable(str(string)) name = str(string) if string != '' else old_exec_module debug( debugPlugin, 'Attaching to process: %s (wait=%s)' % (name, str(wait_for_launch))) sublime.status_message( 'Attaching to process: %s (wait=%s)' % (name, wait_for_launch)) process = target.AttachToProcessWithName( lldb.SBListener(), name, wait_for_launch, error) # attach_info.SetWaitForLaunch(wait_for_launch) # error = lldb.SBError() # debug(debugPlugin, attach_info) # process = target.Attach(attach_info, error) debug(debugPlugin, process) if error.Fail(): sublime.error_message("Attach failed: %s" % error.GetCString()) new_exec_module = target.GetExecutable() if new_exec_module != old_exec_module: debug(debugPlugin, 'Executable module changed from "%s" to "%s".' % \ (old_exec_module, new_exec_module)) new_triple = target.GetTriple() if new_triple != old_triple: debug( debugPlugin, 'Target triple changed from "%s" to "%s".' % (old_triple, new_triple)) # How can we setup the default breakpoints? # We *could* start a new thread with a listener, just for that... else: sublime.error_message('Couldn\'t get a debugging session.')
class LLDBThreadDisassemblyView(LLDBReadOnlyView): __pc_line = 0 settings_keys = [ 'markers.current_line.region_name', 'markers.current_line.scope', 'markers.current_line.scope.crashed', 'markers.current_line.icon' ] __sm = SettingsManager.getSM() eMarkerPCName = __sm.get_default('markers.current_line.region_name', 'lldb.location') eMarkerPCScope = __sm.get_default('markers.current_line.scope', 'bookmark') eMarkerPCScopeCrashed = __sm.get_default( 'markers.current_line.scope.crashed', 'invalid') eMarkerPCIcon = __sm.get_default('markers.current_line.icon', 'bookmark') def __init__(self, view, thread): self.__thread = thread super(LLDBThreadDisassemblyView, self).__init__(view) self.set_name(lldb_disassembly_view_name(thread.GetThreadID())) self.set_scratch() # FIXME: Just make every LLDBCodeView observe the settings. # Another way to do it would be for the class to observe and # then call the appropriate method on all the instances. for k in self.settings_keys: self.__sm.add_observer(k, self.setting_updated) def __repr__(self): return '<%s: name: %s, thread %s, pc_line: %d, content size: %d>' % \ (self.__class__.__name__, self.name(), self.thread, self.pc_line, len(self.content())) ########################################## # View properties. @property def thread(self): return self.__thread @property def pc_line(self): return self.__pc_line ########################################## # Settings observer method. def setting_updated(self, key, old, new): debug( debugSettings | debugViews, 'Updating setting %s from %s to %s. instance: %s' % (key, old, new, self)) if key.startswith('markers.current_line'): # Update all the PC settings. self.__mark_pc(None) self.__class__.eMarkerPCName = self.__sm.get_default( 'markers.current_line.region_name', 'lldb.location') self.__class__.eMarkerPCScope = self.__sm.get_default( 'markers.current_line.scope', 'bookmark') self.__class__.eMarkerPCScopeCrashed = self.__sm.get_default( 'markers.current_line.scope.crashed', 'invalid') self.__class__.eMarkerPCIcon = self.__sm.get_default( 'markers.current_line.icon', 'bookmark') self.__mark_pc(self.__pc_line - 1, False) else: raise Exception( 'Weird key to be updated for LLDBThreadDisassemblyView %s' % key) ########################################## # Update mechanism implementation. def epilogue(self): if self.pc_line != 0: debug(debugViews, 'Marking PC for LLDBDisassemblyView %s' % repr(self)) v = self.base_view() r = v.text_point(self.pc_line, 0) to_mark = [v.line(r)] debug( debugViews, '(' + self.name() + ') adding region: ' + str( (self.eMarkerPCName, to_mark, self.eMarkerPCScope, self.eMarkerPCIcon, sublime.HIDDEN))) self.base_view().add_regions(self.eMarkerPCName, to_mark, self.eMarkerPCScope, self.eMarkerPCIcon, sublime.HIDDEN) self.show(to_mark[0], True) else: debug(debugViews, 'erasing region: %s' % self.eMarkerPCName) self.base_view().erase_regions(self.eMarkerPCName) def updated_content(self): debug(debugViews, 'Updating content for: %s' % repr(self)) # Reset the PC line number self.__pc_line = 0 thread = self.__thread if not thread.IsValid(): return 'Invalid thread. Has it finished its work?' target = thread.GetProcess().GetTarget() pc = thread.GetSelectedFrame().GetPCAddress() function = pc.GetFunction() symbol = pc.GetSymbol() if function.IsValid(): name = function.GetName() start_addr = function.GetStartAddress().GetLoadAddress(target) elif symbol.IsValid(): name = symbol.GetName() start_addr = symbol.GetStartAddress().GetLoadAddress(target) else: name = pc.GetModule().GetFileSpec().GetFilename() start_addr = pc.GetLoadAddress(target) instrs = driver_instance().disassemble_frame(thread.GetSelectedFrame()) if not instrs: return 'Error getting instructions for thread 0x%x: No instructions available.' % thread.GetThreadID( ) pc = driver_instance().get_PC() def get_max_sizes(accum, next): return (max(accum[0], len(next[1])), max(accum[1], len(next[2]))) (max_mnemonic, max_operands) = reduce(get_max_sizes, instrs, (0, 0)) format_str = '%.10s: %*s %*s%s\n' max_mnemonic, max_operands = (int(max_mnemonic), int(max_operands)) result = '%s @ 0x%s:\n' % (name, start_addr) n_instrs = 0 for i in instrs: n_instrs += 1 if len(i) == 3: (addr, mnemonic, ops) = i comment_str = '' elif len(i) == 4: (addr, mnemonic, ops, comment) = i comment_str = '\t; ' + comment else: assert False if pc == addr: self.__pc_line = n_instrs result += format_str % (hex(addr), max_mnemonic, mnemonic, max_operands, ops, comment_str) return result
def setup(self): if lldb_out_view() is None: sm = SettingsManager.getSM() view_name = sm.get_default('i/o.view.name', default_lldb_view_name) set_lldb_out_view(get_lldb_output_view( self.window, view_name)) # for lldb output
def set_lldb_window_layout(cls, window=window_ref()): sm = SettingsManager.getSM() lldb_window_layout = sm.get_default('layout', _default_lldb_window_layout) if lldb_out_view() != None and window.num_groups() != len(lldb_window_layout['cells']): window.run_command('set_layout', lldb_window_layout)
class LLDBCodeView(LLDBView): eRegionPC = 1 << 0 eRegionBreakpointEnabled = 1 << 1 eRegionBreakpointDisabled = 1 << 2 __pc_line = None __bp_lock = Lock() # Settings for the whole class settings_keys = [ 'markers.current_line.region_name', 'markers.current_line.scope', 'markers.current_line.scope.crashed', 'markers.current_line.icon', 'markers.breakpoint.enabled.region_name', 'markers.breakpoint.enabled.scope', 'markers.breakpoint.enabled.type', 'markers.breakpoint.disabled.region_name', 'markers.breakpoint.disabled.scope', 'markers.breakpoint.disabled.type' ] __sm = SettingsManager.getSM() eMarkerPCName = __sm.get_default('markers.current_line.region_name', 'lldb.location') eMarkerPCScope = __sm.get_default('markers.current_line.scope', 'bookmark') eMarkerPCScopeCrashed = __sm.get_default( 'markers.current_line.scope.crashed', 'invalid') eMarkerPCIcon = __sm.get_default('markers.current_line.icon', 'bookmark') eMarkerBreakpointEnabledName = __sm.get_default( 'markers.breakpoint.enabled.region_name', 'lldb.breakpoint.enabled') eMarkerBreakpointEnabledScope = __sm.get_default( 'markers.breakpoint.enabled.scope', 'string') eMarkerBreakpointEnabledIcon = __sm.get_default( 'markers.breakpoint.enabled.type', 'circle') eMarkerBreakpointDisabledName = __sm.get_default( 'markers.breakpoint.disabled.region_name', 'lldb.breakpoint.disabled') eMarkerBreakpointDisabledScope = __sm.get_default( 'markers.breakpoint.disabled.scope', 'bookmark') eMarkerBreakpointDisabledIcon = __sm.get_default( 'markers.breakpoint.disabled.type', 'circle') def __init__(self, view, driver): # FIXME: Split stuff that doesn't have to run on the UI thread. super(LLDBCodeView, self).__init__(view) self.__needs_update = False self.__driver = driver self.__enabled_bps = {} self.__disabled_bps = {} # Get info on current breakpoints for this file self.__populate_breakpoint_lists() if not view.is_loading(): self.__update_bps() self.pre_update() else: debug( debugViews, 'Skipped LLDBCodeView.__update_bps() because view.is_loading is True' ) self.pre_update() self.__needs_update = 'full' # Horrible hack to update the bp # markers as well as the pc marker when the on_load # method calls update on this object # FIXME: Just make every LLDBCodeView observe the settings. # Another way to do it would be for the class to observe and # then call the appropriate method on all the instances. for k in self.settings_keys: self.__sm.add_observer(k, self.setting_updated) def __del__(self): # FIXME: This method won't get called since our observers dict holds a # reference to this object. self.__sm.del_observer(self.settings_updated) def __repr__(self): return '<%s: file_name: %s, needs_update: %s, pc_line: %s, enabled_bps: %s, disable_bps: %s>' % \ (self.__class__.__name__, self.file_name(), str(self._needs_update), str(self.__pc_line), str(self.__enabled_bps), str(self.__disabled_bps)) ########################################## # Settings observer method. def setting_updated(self, key, old, new): debug( debugSettings | debugViews, 'Updating setting %s from %s to %s. instance: %s' % (key, old, new, self)) if key.startswith('markers.current_line'): # Update all the PC settings. self.__mark_pc(None) self.__class__.eMarkerPCName = self.__sm.get_default( 'markers.current_line.region_name', 'lldb.location') self.__class__.eMarkerPCScope = self.__sm.get_default( 'markers.current_line.scope', 'bookmark') self.__class__.eMarkerPCScopeCrashed = self.__sm.get_default( 'markers.current_line.scope.crashed', 'invalid') self.__class__.eMarkerPCIcon = self.__sm.get_default( 'markers.current_line.icon', 'bookmark') self.__mark_pc(self.__pc_line - 1, False) elif key.startswith('markers.breakpoint.enabled'): # Update all the enabled bp settings. self.__mark_regions([], self.eRegionBreakpointEnabled) self.__class__.eMarkerBreakpointEnabledName = self.__sm.get_default( 'markers.breakpoint.enabled.region_name', 'lldb.breakpoint.enabled') self.__class__.eMarkerBreakpointEnabledScope = self.__sm.get_default( 'markers.breakpoint.enabled.scope', 'string') self.__class__.eMarkerBreakpointEnabledIcon = self.__sm.get_default( 'markers.breakpoint.enabled.type', 'circle') # TODO: Check if the settings' on_change method is always called in # the main thread. If not, we'll have to guard the regions # definition v = self.base_view() regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__enabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointEnabled) elif key.startswith('markers.breakpoint.disabled'): # Update all the disabled bp settings. self.__mark_regions([], self.eRegionBreakpointDisabled) self.__class__.eMarkerBreakpointDisabledName = self.__sm.get_default( 'markers.breakpoint.disabled.region_name', 'lldb.breakpoint.disabled') self.__class__.eMarkerBreakpointDisabledScope = self.__sm.get_default( 'markers.breakpoint.disabled.scope', 'bookmark') self.__class__.eMarkerBreakpointDisabledIcon = self.__sm.get_default( 'markers.breakpoint.disabled.type', 'circle') # TODO: Check if the settings' on_change method is always called in # the main thread. If not, we'll have to guard the regions # definition v = self.base_view() regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__disabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointDisabled) else: raise Exception('Weird key to be updated for LLDBCodeView: %s' % key) ########################################## # View properties. @property def __needs_update(self): return self._needs_update @__needs_update.setter def __needs_update(self, value): self._needs_update = value ########################################## # Breakpoint markers' methods. def mark_bp(self, line, is_enabled=True): # {mark,change,unmark}_bp don't update __needs_update because they # immediately update the breakpoint markers """Mark a new breakpoint as enabled/disabled and immediately mark its region.""" self.__add_bps([line], is_enabled) v = self.base_view() if is_enabled: regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__enabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointEnabled) else: regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__disabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointDisabled) def change_bp(self, line, is_enabled): if is_enabled: remove_from = self.__disabled_bps add_to = self.__enabled_bps else: remove_from = self.__enabled_bps add_to = self.__disabled_bps with self.__bp_lock: # The breakpoint must exist in remove_from existing = remove_from[line] if existing == 1: del remove_from[line] else: remove_from[line] = existing - 1 if line in add_to: existing = add_to[line] else: existing = 0 add_to[line] = existing + 1 v = self.base_view() regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__enabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointEnabled) regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__disabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointDisabled) def unmark_bp(self, line, is_enabled=True): """Remove merkings for a breakpoint and update the UI afterwards.""" self.__remove_bps([line], is_enabled) v = self.base_view() if is_enabled: regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__enabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointEnabled) else: regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__disabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointDisabled) ########################################## # Update mechanism implementation. def pre_update(self): """pre_update will perform lldb-related work and get our PC line""" # FIXME: We can't use base_view().is_loading() because Sublime # Text 2 won't even let us query views on another thread (even # read-only properties!!). # This 'full' hack is here to make us wait for the on_load() call # on the LLDBUIListener. # This variable will make us keep __needs_update == 'full' if it was # like that before we ran this function. old_needs_update = self.__needs_update self.__needs_update = False old_pc_line = self.__pc_line self.__pc_line = None debug(debugViews, 'old pc_line: %s' % str(old_pc_line)) thread = self.__driver.current_thread() if not thread: debug(debugViews, 'new pc_line: %s' % str(self.__pc_line)) if self.__pc_line != old_pc_line: self.__needs_update = old_needs_update or True return False for frame in thread: line_entry = frame.GetLineEntry() filespec = line_entry.GetFileSpec() if filespec: filename = filespec.GetDirectory( ) + '/' + filespec.GetFilename() if filename == self.file_name(): self.__pc_line = line_entry.GetLine() debug(debugViews, 'new pc_line: %s' % str(self.__pc_line)) if self.__pc_line != old_pc_line or old_needs_update == 'full': self.__needs_update = old_needs_update or True return True debug(debugViews, 'new pc_line: %s' % str(self.__pc_line)) if self.__pc_line != old_pc_line or old_needs_update == 'full': self.__needs_update = old_needs_update or True return False def update(self): debug( debugViews, 'Updating LLDBCodeView. needs_update: %s' % str(self.__needs_update)) if self.__needs_update and not self.base_view().is_loading(): # Hack so we update the bps when updating the view for the first time. if self.__needs_update == 'full': self.__update_bps() if self.__pc_line is not None: self.__mark_pc(self.__pc_line - 1, True) else: self.__mark_pc(None) # For now, bp-marking functions will immediately update the # view. We don't need to update it when the view is dirty. # self.__update_bps() self.__needs_update = False else: debug( debugViews, 'LLDBCodeView: didn\'t need an update (or view was loading): %s' % repr(self)) def stop(self): self.pre_update() # This will set pc_line to None self.__enabled_bps = {} self.__disabled_bps = {} def to_ui(): debug(debugViews, 'executing UI code for LLDBCodeView.stop()') self.update() self.__update_bps() sublime.set_timeout(to_ui, 0) ########################################## # Private LLDBCodeView methods def __mark_regions(self, regions, type): if type == self.eRegionPC: self.__mark_or_delete_regions(self.eMarkerPCName, regions, self.eMarkerPCScope, self.eMarkerPCIcon, sublime.HIDDEN) elif type == self.eRegionBreakpointEnabled: self.__mark_or_delete_regions(self.eMarkerBreakpointEnabledName, regions, self.eMarkerBreakpointEnabledScope, self.eMarkerBreakpointEnabledIcon, sublime.HIDDEN) elif type == self.eRegionBreakpointDisabled: self.__mark_or_delete_regions(self.eMarkerBreakpointDisabledName, regions, self.eMarkerBreakpointDisabledScope, self.eMarkerBreakpointDisabledIcon, sublime.HIDDEN) def __mark_or_delete_regions(self, name, regions, scope, icon, options): if len(regions) > 0: debug( debugViews, '(%s) adding regions: %s' % (self.file_name(), (name, regions, scope, icon, options))) self.base_view().add_regions(name, regions, scope, icon, options) else: debug(debugViews, 'erasing regions: %s' % name) self.base_view().erase_regions(name) def __mark_pc(self, line, show=False): debug(debugViews, 'Marking PC for LLDBCodeView: %s' % repr(self)) v = self.base_view() if line is None: to_mark = [] else: to_mark = [v.line(v.text_point(line, 0))] self.__mark_regions(to_mark, self.eRegionPC) if show and to_mark: self.show(to_mark[0], True) def __populate_breakpoint_lists(self): file_bp_locs = self.__driver.get_breakpoint_locations_for_file( self.file_name()) def line_from_bp_loc(bp_loc): line_entry = bp_loc.GetAddress().GetLineEntry() return line_entry.GetLine() enabled_bp_lines = [] disabled_bp_lines = [] for bp_loc in file_bp_locs: if bp_loc.IsEnabled(): enabled_bp_lines.append(line_from_bp_loc(bp_loc)) else: disabled_bp_lines.append(line_from_bp_loc(bp_loc)) self.__add_bps(enabled_bp_lines, True) self.__add_bps(disabled_bp_lines, False) def __add_bps(self, lines, are_enabled=True): """Adds breakpoints (enabled or disabled) to the view. __update_bps() must be called afterwards to refresh the UI.""" if len(lines) > 0: self.__needs_update = True if are_enabled: add_to = self.__enabled_bps else: add_to = self.__disabled_bps with self.__bp_lock: # We shouldn't have that many breakpoints for this to be a # problem. If the lock becomes a problem, we can lock for each # breakpoint. for line in lines: if line in add_to: existing = add_to[line] else: existing = 0 add_to[line] = existing + 1 def __remove_bps(self, lines, are_enabled=True): """Removes breakpoints (enabled or disabled) from the view. __update_bps() must be called afterwards to refresh the UI.""" if len(lines) > 0: self.__needs_update = True if are_enabled: remove_from = self.__enabled_bps else: remove_from = self.__disabled_bps with self.__bp_lock: for line in lines: existing = remove_from[line] if existing == 1: del remove_from[line] else: remove_from[line] = existing - 1 def __update_bps(self): v = self.base_view() regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__enabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointEnabled) regions = map(lambda line: v.line(v.text_point(line - 1, 0)), self.__disabled_bps.keys()) self.__mark_regions(regions, self.eRegionBreakpointDisabled)
def setup(self): if lldb_out_view() is None: sm = SettingsManager.getSM() view_name = sm.get_default('i/o.view.name', default_lldb_view_name) set_lldb_out_view(get_lldb_output_view(self.window, view_name)) # for lldb output