Skip to content

Commit cd5f5cb

Browse files
Ingo HeimbachIngo Heimbach
authored andcommitted
The LLDB frontend is capable of copying the current stacktrace to
clipboard in a format that can be parsed by vim-unstack.
1 parent a2e2dcd commit cd5f5cb

3 files changed

Lines changed: 57 additions & 10 deletions

File tree

autoload/vebugger/lldb.vim

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ let s:script_dir_path=expand('<sfile>:p:h')
22

33
function! vebugger#lldb#start(binaryFile,args)
44
let l:debuggerExe=vebugger#util#getToolFullPath('python','lldb','python2')
5-
let l:debugger=vebugger#std#startDebugger(shellescape(l:debuggerExe)
6-
\.' '.s:script_dir_path.'/lldb_wrapper.py '.fnameescape(a:binaryFile))
5+
let l:debuggerStartCommand = shellescape(l:debuggerExe)
6+
\.' '.s:script_dir_path.'/lldb_wrapper.py '.fnameescape(a:binaryFile)
7+
if exists('g:vebugger_copy_stacktrace_to_clipboard_lldb') &&
8+
\ g:vebugger_copy_stacktrace_to_clipboard_lldb
9+
let l:debuggerStartCommand .= ' --stacktrace_to_clipboard'
10+
endif
11+
let l:debugger=vebugger#std#startDebugger(l:debuggerStartCommand)
712

813
let l:debugger.state.lldb={}
914

autoload/vebugger/lldb_wrapper.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ def remove_breakpoint(self, filename, linenumber):
7878
del self._location_to_id[location]
7979
return breakpoint_id
8080

81-
def __init__(self, executable):
81+
def __init__(self, executable, stacktrace_to_clipboard=False):
8282
self._executable = executable
83+
self._stacktrace_to_clipboard = stacktrace_to_clipboard
8384
self._debugger = lldb.SBDebugger.Create()
8485
self._debugger.SetAsync(False)
8586
self._command_interpreter = self._debugger.GetCommandInterpreter()
@@ -143,6 +144,26 @@ def clear(arguments):
143144
arguments = parts[1:]
144145
locals()[command](arguments)
145146

147+
def _copy_stacktrace_to_clipboard(self, stacktrace):
148+
stacktrace_text = '\n'.join('File "{}", line {:d},'.format(file, line) for file, line in reversed(stacktrace))
149+
platform_system = platform.system()
150+
process = None
151+
if platform_system == 'Darwin':
152+
process = subprocess.Popen('pbcopy', env={'LANG': 'en_US.UTF-8'}, stdin=subprocess.PIPE)
153+
else:
154+
# assume we are running on Linux
155+
for cmd in (['xclip', '-selection', 'clipboard'], ['xsel', '--clipboard']):
156+
try:
157+
process = subprocess.Popen(cmd, stdin=subprocess.PIPE)
158+
break
159+
except OSError:
160+
pass
161+
else:
162+
print(prefix_output('Stacktrace cannot be copied to clipboard! Either install xclip or xsel.',
163+
'warning: '))
164+
if process is not None:
165+
process.communicate(stacktrace_text.encode('utf-8'))
166+
146167
@property
147168
def debugger_output(self):
148169
if self._last_debugger_return_obj is not None:
@@ -154,7 +175,7 @@ def debugger_output(self):
154175

155176
@property
156177
def where(self):
157-
def extract_where(backtrace):
178+
def extract_where(backtrace, collect_complete_stacktrace=False):
158179
where = None
159180
pattern = re.compile('at:([^:]+):(\d+)')
160181
backtrace_lines = backtrace.split('\n')
@@ -164,16 +185,25 @@ def extract_where(backtrace):
164185
filepath = match_obj.group(1)
165186
linenumber = int(match_obj.group(2))
166187
if os.access(filepath, os.R_OK):
167-
where = FilePosition(filepath, linenumber)
168-
break
188+
if collect_complete_stacktrace:
189+
if where is None:
190+
where = []
191+
where.append(FilePosition(filepath, linenumber))
192+
else:
193+
where = FilePosition(filepath, linenumber)
194+
break
169195
return where
170196

171197
where = None
172198
self.run_command('bt')
173199
debugger_output = self.debugger_output
174200
if debugger_output is not None:
175-
where = extract_where(debugger_output)
176-
return where
201+
where = extract_where(debugger_output, self._stacktrace_to_clipboard)
202+
if self._stacktrace_to_clipboard and where is not None:
203+
self._copy_stacktrace_to_clipboard(where)
204+
return where[0]
205+
else:
206+
return where
177207

178208
@property
179209
def program_stdout(self):
@@ -223,9 +253,9 @@ def main():
223253
if len(sys.argv) < 2:
224254
sys.stderr.write('An executable is needed as an argument.\n')
225255
sys.exit(1)
226-
227256
executable = sys.argv[1]
228-
debugger = Debugger(executable)
257+
stacktrace_to_clipboard = (len(sys.argv) > 2 and sys.argv[2] == '--stacktrace_to_clipboard')
258+
debugger = Debugger(executable, stacktrace_to_clipboard)
229259

230260
try:
231261
while True:

doc/vebugger.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ Example: >
118118
let g:vebugger_use_tags=1
119119
<
120120

121+
The LLDB frontend is capable of copying the current debugger stacktrace to
122+
clipboard in a format that can be read by the vim-unstack plugin (see
123+
https://github.com/mattboehm/vim-unstack for more information). To activate
124+
this feature set *g:vebugger_copy_stacktrace_to_clipboard_lldb* option:
125+
126+
Example: >
127+
let g:vebugger_copy_stacktrace_to_clipboard_lldb=1
128+
<
129+
130+
On Linux, you need either xclip or xsel for this feature to work.
131+
132+
121133
LAUNCHING DEBUGGERS *vebugger-launching*
122134

123135
A debugger's implementation is responsible for starting it. The standard is to

0 commit comments

Comments
 (0)