Skip to content

Commit

Permalink
Fix #7 (gdb only): Merge branch 'breakpoint'
Browse files Browse the repository at this point in the history
  • Loading branch information
sakhnik committed Mar 17, 2018
2 parents d41b3d8 + 8c6a562 commit 37faa8a
Show file tree
Hide file tree
Showing 11 changed files with 373 additions and 212 deletions.
25 changes: 16 additions & 9 deletions autoload/nvimgdb.vim
Expand Up @@ -4,11 +4,13 @@ sign define GdbCurrentLine text=⇒

" Count of active debugging views
let g:nvimgdb_count = 0

let s:plugin_dir = expand('<sfile>:p:h:h')

" gdb specifics
let s:backend_gdb = {
\ 'init': ['set confirm off', 'set pagination off'],
\ 'init': ['set confirm off',
\ 'set pagination off',
\ 'source ' . s:plugin_dir . '/lib/gdb_commands.py'],
\ 'paused': [
\ ['Continuing.', 'continue'],
\ ['\v[\o32]{2}([^:]+):(\d+):\d+', 'jump'],
Expand Down Expand Up @@ -75,15 +77,19 @@ endfunction

" Transition "paused" -> "paused": jump to the frame location
function s:GdbPaused_breakpoint(num, skip, file, line, ...) dict
if !exists("self._pending_breakpoint_file")
return
if exists("self._pending_breakpoint_file")
let file_name = self._pending_breakpoint_file
let linenr = self._pending_breakpoint_linenr
unlet self._pending_breakpoint_file
unlet self._pending_breakpoint_linenr
else
let linenr = a:line
let file_name = t:gdb._impl.FindSource(a:file)
if empty(file_name)
return
endif
endif

let file_name = self._pending_breakpoint_file
let linenr = self._pending_breakpoint_linenr
unlet self._pending_breakpoint_file
unlet self._pending_breakpoint_linenr

" Remember the breakpoint number
let file_breakpoints = get(self._breakpoints, file_name, {})
let file_breakpoints[linenr] = a:num
Expand Down Expand Up @@ -317,6 +323,7 @@ endfunction

function! nvimgdb#Spawn(backend, client_cmd)
let gdb = s:InitMachine(a:backend, s:Gdb)
exe 'let gdb._impl = nvimgdb#' . a:backend . '#GetImpl()'
let gdb._initialized = 0
" window number that will be displaying the current file
let gdb._jump_window = 1
Expand Down
29 changes: 29 additions & 0 deletions autoload/nvimgdb/gdb.vim
@@ -0,0 +1,29 @@
let s:root_dir = expand('<sfile>:p:h:h:h')
let s:impl = {}

function s:DoFindSource(file)
exe 'py3 import sys'
exe 'py3 sys.argv = ["' . a:file . '"]'
exe 'py3file ' . s:root_dir . '/lib/gdb_find_source.py'
return return_value
endfunction

function s:impl.FindSource(file)
if filereadable(a:file)
return fnamemodify(a:file, ':p')
endif

let ret = s:DoFindSource(a:file)
if !len(ret)
return ""
elseif len(ret) == 1
return ret[0]
else
" TODO: inputlist()
return ""
endif
endfunction

function! nvimgdb#gdb#GetImpl()
return s:impl
endfunction
9 changes: 9 additions & 0 deletions autoload/nvimgdb/lldb.vim
@@ -0,0 +1,9 @@
let s:impl = {}

function s:impl.FindSource(file)
return ""
endfunction

function! nvimgdb#lldb#GetImpl()
return s:impl
endfunction
14 changes: 14 additions & 0 deletions lib/gdb_commands.py
@@ -0,0 +1,14 @@
import re
import socket

class InfoSources(gdb.Command):
def __init__(self):
super(InfoSources, self).__init__("nvim-gdb-info-sources", gdb.COMMAND_NONE)

def invoke(self, arg, from_tty):
output = gdb.execute('info sources', from_tty=False, to_string=True)
sources = "\n".join(sorted([f[0][::-1] for f in re.finditer(r'/[^, \n]+', output)]))
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.sendto(sources.encode('utf-8'), arg)

InfoSources()
45 changes: 45 additions & 0 deletions lib/gdb_find_source.py
@@ -0,0 +1,45 @@
import vim
import socket
import sys
import os

server_address = '/tmp/nvim-gdb-python-socket'

# Make sure the socket does not already exist
try:
os.unlink(server_address)
except OSError:
if os.path.exists(server_address):
raise

# Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.bind(server_address)
sock.settimeout(0.5)

vim.command('call nvimgdb#Send("nvim-gdb-info-sources %s")' % server_address)

data, addr = sock.recvfrom(65536)
lines = data.decode('utf-8').splitlines()
target = os.path.normpath(sys.argv[0])
target_min_len = len(os.path.basename(target))
target = target[::-1]

def LongestCommonPrefix(a, b):
n = min(len(a), len(b))
for i in range(n):
if a[i] != b[i]:
return i
return n

m = target_min_len
result = []
for l in lines:
x = LongestCommonPrefix(target, l)
if x > m:
m = x
result = [l[::-1]]
elif x == m:
result.append(l[::-1])

vim.command('let return_value = ' + str(result))
29 changes: 29 additions & 0 deletions lib/lldb_sources.py
@@ -0,0 +1,29 @@
#!/usr/bin/python

import lldb
import os


def dump_module_sources(module, result):
if module:
files = set()
print >> result, "Module: %s" % (module.file)
for compile_unit in module.compile_units:
for line_entry in compile_unit:
files.add(os.path.normpath(str(line_entry.GetFileSpec())))
for f in files:
print >> result, " %s" % str(f)


def info_sources(debugger, command, result, dict):
description = '''This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied.'''
target = debugger.GetSelectedTarget()
for module in target.modules:
dump_module_sources(module, result)


def __lldb_init_module(debugger, dict):
# Add any commands contained in this module to LLDB
debugger.HandleCommand(
'command script add -f lldb_sources.info_sources info_sources')
print 'The "info_sources" command has been installed, type "help info_sources" or "info_sources --help" for detailed help.'
43 changes: 43 additions & 0 deletions test/engine.py
@@ -0,0 +1,43 @@
import os
import time
import re
from neovim import attach

# Neovim proxy
class Engine:

delay = 0.5

def __init__(self):
os.system('g++ -g src/test.cpp')
addr = os.environ.get('NVIM_LISTEN_ADDRESS')
if addr:
self.nvim = attach('socket', path=addr)
else:
self.nvim = attach('child', argv=["/usr/bin/env", "nvim", "--embed", "-n", "-u", "init.vim"])

def Command(self, cmd):
self.nvim.command(cmd)
time.sleep(Engine.delay)

def GetSigns(self):
out = self.nvim.eval('execute("sign place")')
curline = [int(l) for l in re.findall(r'line=(\d+)\s+id=\d+\s+name=GdbCurrentLine', out)]
assert(len(curline) <= 1)
breaks = [int(l) for l in re.findall(r'line=(\d+)\s+id=\d+\s+name=GdbBreakpoint', out)]
return (curline[0] if curline else -1), breaks

def KeyStrokeL(self, keys):
self.nvim.input(keys)
time.sleep(Engine.delay)

def KeyStroke(self, keys):
self.nvim.feedkeys(keys, 't')
time.sleep(Engine.delay)

def Eval(self, expr):
return self.nvim.eval(expr)

def CountBuffers(self):
return sum(self.Eval('buflisted(%d)' % (i+1)) for i in range(0, self.Eval('bufnr("$")')))

File renamed without changes.
161 changes: 161 additions & 0 deletions test/test_10_generic.py
@@ -0,0 +1,161 @@
#!/usr/bin/env python

import unittest
import engine


eng = engine.Engine()
subtests = {"gdb": [' dd', '\n'], "lldb": [' dl', '\n']}


class TestGdb(unittest.TestCase):

def test_10_quit(self):
''' => Verify that the session exits correctly on window close '''
cases = [["<esc>", ":GdbDebugStop<cr>"], ["<esc>","ZZ"], ["<esc>","<c-w>w","ZZ"]]
numBufs = eng.CountBuffers()
for c in cases:
with self.subTest(case=c):
for k in subtests['gdb']:
eng.KeyStrokeL(k)
for k in c:
eng.KeyStrokeL(k)
self.assertEqual(1, eng.Eval('tabpagenr("$")'))
# Check that no new buffers have left
self.assertEqual(numBufs, eng.CountBuffers())

def test_20_generic(self):
''' => Test a generic use case '''
for backend, launch in subtests.items():
with self.subTest(backend=backend):
for k in launch:
eng.KeyStroke(k)
eng.KeyStroke('tbreak main\n')
eng.KeyStroke('run\n')
eng.KeyStrokeL('<esc>')

cur, breaks = eng.GetSigns()
self.assertEqual(16, cur)
self.assertFalse(breaks)

eng.KeyStrokeL('<f10>')
cur, breaks = eng.GetSigns()
self.assertEqual(18, cur)
self.assertFalse(breaks)

eng.KeyStrokeL('<f11>')
cur, breaks = eng.GetSigns()
self.assertEqual(9, cur)
self.assertFalse(breaks)

eng.KeyStrokeL('<f12>')
cur, breaks = eng.GetSigns()
self.assertEqual(16, cur)
self.assertFalse(breaks)

eng.KeyStrokeL('<f5>')
cur, breaks = eng.GetSigns()
self.assertEqual(-1, cur)
self.assertFalse(breaks)

eng.Command('GdbDebugStop')

def test_30_breakpoint(self):
''' => Test toggling breakpoints '''
for backend, launch in subtests.items():
with self.subTest(backend=backend):
for k in launch:
eng.KeyStroke(k)
eng.KeyStrokeL('<esc><c-w>k')
eng.KeyStroke(":e src/test.cpp\n")
eng.KeyStrokeL(':4<cr>')
eng.KeyStrokeL('<f8>')
cur, breaks = eng.GetSigns()
self.assertEqual(-1, cur)
self.assertListEqual([4], breaks)

eng.Command("GdbRun")
cur, breaks = eng.GetSigns()
self.assertEqual(4, cur)
self.assertListEqual([4], breaks)

eng.KeyStrokeL('<f8>')
cur, breaks = eng.GetSigns()
self.assertEqual(4, cur)
self.assertFalse(breaks)

eng.Command('GdbDebugStop')

def test_35_breakpoint_cleanup(self):
''' => Verify that breakpoints are cleaned up after session end'''
launch = subtests['gdb']
for k in launch:
eng.KeyStroke(k)
eng.KeyStrokeL('<esc><c-w>k')
eng.KeyStroke(":e src/test.cpp\n")
eng.KeyStrokeL(':4<cr>')
eng.KeyStrokeL('<f8>')
cur, breaks = eng.GetSigns()
self.assertEqual(-1, cur)
self.assertListEqual([4], breaks)

eng.Command("GdbDebugStop")
cur, breaks = eng.GetSigns()
self.assertEqual(-1, cur)
self.assertFalse(breaks)

def test_40_multiview(self):
''' => Test multiple views '''
# Launch GDB first
for k in subtests['gdb']:
eng.KeyStroke(k)
eng.KeyStroke('tbreak main\n')
eng.KeyStroke('run\n')
eng.KeyStrokeL('<esc>')
eng.KeyStrokeL('<c-w>w')
eng.KeyStrokeL(':10<cr>')
eng.KeyStrokeL('<f8>')
eng.KeyStrokeL('<f10>')
eng.KeyStrokeL('<f11>')

cur, breaks = eng.GetSigns()
self.assertEqual(9, cur)
self.assertEqual([10], breaks)

# Then launch LLDB
for k in subtests['lldb']:
eng.KeyStroke(k)
eng.KeyStroke('tbreak main\n')
eng.KeyStroke('run\n')
eng.KeyStrokeL('<esc>')
eng.KeyStrokeL('<c-w>w')
eng.KeyStrokeL(':4<cr>')
eng.KeyStrokeL('<f8>')
eng.KeyStrokeL(':11<cr>')
eng.KeyStrokeL('<f8>')
eng.KeyStrokeL('<f10>')

cur, breaks = eng.GetSigns()
self.assertEqual(18, cur)
self.assertEqual([4, 11], breaks)

# Switch to GDB
eng.KeyStrokeL('2gt')
cur, breaks = eng.GetSigns()
self.assertEqual(9, cur)
self.assertEqual([10], breaks)

# Quit GDB
eng.KeyStrokeL('ZZ')

# Switch back to LLDB
cur, breaks = eng.GetSigns()
self.assertEqual(18, cur)
self.assertEqual([4, 11], breaks)

# Quit LLDB
eng.KeyStrokeL('ZZ')


if __name__ == "__main__":
unittest.main()

0 comments on commit 37faa8a

Please sign in to comment.