1 from __future__
import unicode_literals
3 import tokenizer
, py_console
4 import time
, os
, pty
, codecs
, re
6 def extract_filename(fn
):
11 def __init__(self
, command
, *args
, **kwargs
):
12 self
.command
= command
13 self
.proc
= Proc("LC_ALL=C gdb --args " + command
, shell
=True)
14 self
.debugf
= open('debug.log', 'w')
15 self
.compilation_directory
= ''
20 self
.cached_stdout
= ''
21 self
.cached_stderr
= ''
24 if self
.proc
.canread(self
.proc
.stdout()): break
25 if self
.proc
.canread(self
.proc
.stderr()): break
27 # disable getting the output split into single pages and interacting
28 self
.send('set pagination off')
30 # disable printing of strings interrupted by <repeats x times>
31 self
.send('set print repeats 0xfffffffe')
33 master
, slave
= pty
.openpty()
34 self
.send('set inferior-tty ' + os
.ttyname(slave
))
36 self
.pty_master
= master
37 self
.pty_slave
= slave
38 self
._reload
_sources
()
39 self
.cached_stdout
= s
40 self
.cached_stderr
= t
41 def add_bp(self
, file, line
):
42 file = self
.find_sourcefile(file)
43 if not file in self
.breakpoints
: self
.breakpoints
[file] = []
44 self
.breakpoints
[file].append(line
)
45 def _set_exit_code(self
, ec
):
46 self
.proc
.exitcode
= ec
47 def _reload_sources(self
):
48 self
.send('info sources')
49 s
= self
.raw_read('stdout')
53 if line
.startswith('Source files for'): continue
54 line
= line
.rstrip('\n')
55 if line
== '': continue
56 items
= line
.split(', ')
57 for item
in items
: self
.sources
.append(item
)
58 def set_sourcefile(self
, file):
59 if len(file) and not file.startswith('/'):
60 file = self
.compilation_directory
+ '/' + file
61 self
.debug("sourcefile " + file)
62 self
.sourcefile
= file
63 def find_sourcefile_single(self
, file):
65 if len(file) and not file.startswith('/'):
66 full
= self
.compilation_directory
+ '/' + file
67 for s
in self
.sources
:
68 if s
== file or (full
and s
== full
): return s
71 for s
in self
.sources
:
72 if s
.endswith(tmp
): return s
74 def find_sourcefile(self
, file):
75 if self
.sourcefile
== file:
77 if not file.startswith('/') and self
.compilation_directory
+ '/' + file == self
.sourcefile
:
78 return self
.sourcefile
79 s
= self
.find_sourcefile_single(file)
81 self
._reload
_sources
()
82 s
= self
.find_sourcefile_single(file)
85 def _consume_cached(self
, which
):
87 res
= self
.cached_stdout
88 self
.cached_stdout
= ''
90 res
= self
.cached_stderr
91 self
.cached_stderr
= ''
95 return self
.pty_master
96 def istdout_canread(self
):
97 return self
.proc
.canread(self
.pty_master
)
98 def debug(self
, text
):
99 self
.debugf
.write(text
+ '\n')
101 def raw_read(self
, filename
):
102 if self
.get_exitcode() is not None: return ''
103 s
= self
._consume
_cached
(filename
)
104 if filename
== 'stdout':
105 f
= self
.proc
.stdout()
107 f
= self
.proc
.stderr()
108 while self
.proc
.canread(f
):
114 s
= self
.raw_read('stdout')
115 #if '[Inferior 1 (process 19071) exited normally]
116 # [Inferior 1 (process 19224) exited with code 01]
117 lines
= s
.split('\n')
119 if l
.startswith('The program is not being run.'):
120 self
._set
_exit
_code
(-3)
121 if l
.startswith('[Inferior 1 (process '):
122 if l
.endswith('exited normally]'): self
.proc
.exitcode
= 0
123 if ') exited with code ' in l
and l
.endswith(']'):
124 self
._set
_exit
_code
(int(l
.split(' ')[-1].rstrip(']'), 10))
125 if not self
.proc
.exitcode
and lines
[-1] != '(gdb) ':
126 s
+= self
.proc
.read_until(self
.proc
.stdout(), '(gdb) ')
127 lines
= s
.split('\n')
130 if l
.startswith('Breakpoint ') or l
.startswith('Temporary breakpoint '):
135 if le
.find(':') == -1:
136 # dont have a file:lineno tuple at the end, it's the confirmation a breakpoint has been set
137 if len(a
) > 4 and a
[-4] == u
'file' and a
[-2] == u
'line':
140 if not l
.startswith('Temporary'):
141 self
.add_bp(file, int(lineno
))
144 file, lineno
= le
.split(':')
146 self
.set_sourcefile(self
.find_sourcefile(file))
147 self
.lineno
= int(lineno
)
149 t
= self
.raw_read('stderr')
150 lines
= t
.split('\n')
152 if len(l
): self
.debug('E ' + l
)
153 if l
.startswith('During symbol reading, incomplete CFI data'):
154 self
._set
_exit
_code
(-1)
155 if l
.startswith('Cannot find bounds of current function'):
156 self
._set
_exit
_code
(-2)
158 def get_exitcode(self
):
159 return self
.proc
.exitcode
160 def send(self
, command
):
161 if self
.get_exitcode() is not None: return
162 self
.proc
.stdin().write(command
+ '\n')
164 def set_source_from_ouput(self
, s
):
167 if x
.startswith('Located in '):
168 self
.sourcefile
= x
[len('Located in '):]
169 elif x
.startswith('Compilation directory is '):
170 self
.compilation_directory
= x
[len('Compilation directory is '):]
171 return self
.sourcefile
177 for i
in xrange(1, len(sys
.argv
)):
193 gdb
.send('info source')
195 source
= gdb
.set_source_from_ouput(s
)
197 gdb
.send('tbreak main')
208 from prompt_toolkit
import Application
209 from prompt_toolkit
.buffer import Buffer
210 from prompt_toolkit
.widgets
import TextArea
, Label
, MenuContainer
, MenuItem
211 from prompt_toolkit
.layout
.containers
import HSplit
, VSplit
, Window
, ScrollOffsets
, ConditionalContainer
212 from prompt_toolkit
.layout
.controls
import BufferControl
, FormattedTextControl
213 from prompt_toolkit
.layout
.layout
import Layout
214 from prompt_toolkit
.application
import get_app
215 from prompt_toolkit
.key_binding
import KeyBindings
, merge_key_bindings
216 from prompt_toolkit
.layout
.dimension
import LayoutDimension
, Dimension
217 from prompt_toolkit
.lexers
import PygmentsLexer
218 from prompt_toolkit
.document
import Document
219 from prompt_toolkit
.selection
import SelectionState
220 from prompt_toolkit
.filters
import Condition
221 from prompt_toolkit
.history
import InMemoryHistory
222 from pygments
.lexers
.c_cpp
import CLexer
223 from pygments
.token
import Token
224 from prompt_toolkit
.styles
import Style
, style_from_pygments_cls
, merge_styles
225 from editor_style
import CodeviewStyle
233 def append(self
, key
, value
):
234 self
.order
.append(key
)
235 self
.values
[key
] = value
236 def update(self
, values
):
237 if self
.order
is None or len(self
.order
) == 0:
238 self
.order
= sorted(values
.keys())
240 self
._changed
= self
.order
243 while i
< len(self
.order
):
244 if not self
.order
[i
] in values
:
250 if not key
in self
.order
: new
.append(key
)
251 elif not self
.values
[key
] == values
[key
]: changed
.append(key
)
253 self
.order
.extend(new
)
256 self
._changed
= changed
257 def was_changed(self
, key
):
258 return self
._changed
is not None and key
in self
._changed
259 def get_value_by_index(self
, index
):
260 if index
< len(self
):
261 return self
[self
.order
[index
]]
264 if self
.order
is None: return 0
265 return len(self
.order
)
267 if self
.order
is None: return
268 for x
in xrange(len(self
.order
)):
270 def __getitem__(self
, index
):
271 if index
in self
.values
:
272 return self
.values
[index
]
275 from prompt_toolkit
.mouse_events
import MouseEventType
276 def if_mousedown(handler
):
277 def handle_if_mouse_down(mouse_event
):
278 if mouse_event
.event_type
== MouseEventType
.MOUSE_DOWN
:
279 return handler(mouse_event
)
281 return NotImplemented
282 return handle_if_mouse_down
284 class SidebarControl(FormattedTextControl
):
285 def move_cursor_down(self
):
286 get_app().my
.controls
[name
].selected_option_index
+= 1
287 def move_cursor_up(self
):
288 get_app().my
.controls
[name
].selected_option_index
-= 1
289 def focus_on_click(self
):
292 def get_config_file_name():
294 return os
.getenv('HOME') + '/.gdbpimp.conf.json'
295 def write_config_file(s
):
296 with
open(get_config_file_name(), 'w') as h
: h
.write(s
)
297 def get_config_file_contents():
299 with
open(get_config_file_name(), 'r') as h
: s
= h
.read()
302 def json_load_config():
304 try: return json
.loads(get_config_file_contents())
308 get_app().my
.saved_config
= json_load_config() or {}
309 if get_app().my
.gdb
.command
in get_app().my
.saved_config
:
310 if 'exprs' in get_app().my
.saved_config
[get_app().my
.gdb
.command
]:
311 get_app().my
.exprs_dict
= get_app().my
.saved_config
[get_app().my
.gdb
.command
]['exprs']
313 get_app().my
.saved_config
[get_app().my
.gdb
.command
] = {
314 'exprs' : get_app().my
.exprs_dict
,
317 write_config_file(json
.dumps(get_app().my
.saved_config
))
319 def sidebar(name
, kvdict
):
320 # shamelessly stolen and adapted from ptpython/layout.py
322 _VAL_WIDTH
= 14 # sufficient to print "0x" + 12hex chars for a 48bit memory address
323 _CTR_WIDTH
= _MAX_KEY_WIDTH
+ _VAL_WIDTH
324 def center_str(s
, w
):
339 def pad_or_cut(s
, w
):
340 if len(s
) > w
: s
= s
[:w
]
341 while len(s
) < w
: s
+= ' '
344 def get_text_fragments():
346 def append_title(title
):
348 def focus_from_title(mouse_event
):
349 get_app().my
.set_focus(name
)
350 foc
= ',focused' if get_app().my
.focused_control
== name
else ''
352 ('class:sidebar', ' ', focus_from_title
),
353 ('class:sidebar.title'+foc
, center_str(title
, _CTR_WIDTH
), focus_from_title
),
354 ('class:sidebar', '\n'),
356 def append(index
, label
, status
, max_key_len
):
357 key_len
= min(_MAX_KEY_WIDTH
, max_key_len
)
358 val_len
= _CTR_WIDTH
- key_len
359 selected
= get_app().my
.controls
[name
].selected_option_index
== index
362 def select_item(mouse_event
):
363 get_app().my
.set_focus(name
)
364 get_app().my
.controls
[name
].selected_option_index
= index
367 def trigger_vardetail(mouse_event
):
368 get_app().my
.set_focus(name
)
369 get_app().my
.controls
[name
].selected_option_index
= index
370 vardetails_toggle_on_off()
372 odd
= 'odd' if index
%2 != 0 else ''
373 sel
= ',selected' if selected
else ''
374 chg
= ',changed' if kvdict().was_changed(label
) else ''
375 tokens
.append(('class:sidebar' + sel
, '>' if selected
else ' '))
376 tokens
.append(('class:sidebar.label' + odd
+ sel
, pad_or_cut(label
, key_len
), select_item
))
377 tokens
.append(('class:sidebar.status' + odd
+ sel
+ chg
, pad_or_cut(status
, val_len
), trigger_vardetail
))
379 tokens
.append(('[SetCursorPosition]', ''))
380 tokens
.append(('class:sidebar', '<' if selected
else ' '))
381 tokens
.append(('class:sidebar', '\n'))
386 mydict
= kvdict() if callable(kvdict
) else kvdict
388 for key
in mydict
: max_key_len
= max(max_key_len
, len(key
))
391 append(i
, key
, '%s' % values
[0], max_key_len
+1)
393 tokens
.pop() # Remove last newline.
395 get_app().my
.controls
[name
].height
= Dimension(min=2, max=i
+1 if i
>1 else 2)
399 SidebarControl(get_text_fragments
),
400 style
='class:sidebar',
401 width
=Dimension
.exact(_CTR_WIDTH
+2),
402 height
=Dimension(min=2),
403 scroll_offsets
=ScrollOffsets(top
=1, bottom
=1))
404 ctrl
.selected_option_index
= 0
407 def vardetails_toggle_on_off():
409 if app
.my
.controls
['vardetails'].text
== '':
410 app
.my
.controls
['vardetails'].text
= 'X'
411 app
.my
.controls
['vardetails'].update()
412 else: app
.my
.controls
['vardetails'].text
= ''
414 def load_sidebar_bindings(name
):
415 #sidebar_visible = Condition(lambda: config.show_sidebar)
416 sidebar_visible
= Condition(lambda: True)
417 sidebar_focused
= Condition(lambda: get_app().my
.focused_control
== name
)
418 sidebar_handles_keys
= sidebar_visible
& sidebar_focused
419 bindings
= KeyBindings()
420 handle
= bindings
.add
421 @handle('up', filter=sidebar_handles_keys
)
423 event
.app
.my
.controls
[name
].selected_option_index
= (
424 (event
.app
.my
.controls
[name
].selected_option_index
- 1) % len(vars(event
.app
.my
)[name
]))
425 # the vars thing is so we can point to app.my.locals OrderedDict
426 # but when we repurpose the sidebar, to something else
427 @handle('down', filter=sidebar_handles_keys
)
429 event
.app
.my
.controls
[name
].selected_option_index
= (
430 (event
.app
.my
.controls
[name
].selected_option_index
+ 1) % len(vars(event
.app
.my
)[name
]))
432 @handle('enter', filter=sidebar_handles_keys
)
434 vardetails_toggle_on_off()
437 def load_inputbar_bindings():
438 inputbar_focused
= Condition(lambda: get_app().my
.focused_control
== 'input')
439 bindings
= KeyBindings()
440 handle
= bindings
.add
441 @handle('up', filter=inputbar_focused
)
443 event
.app
.my
.controls
['input'].content
.buffer.auto_up()
444 @handle('down', filter=inputbar_focused
)
446 event
.app
.my
.controls
['input'].content
.buffer.auto_down()
451 def codeview_line_prefix(line_number
, wrap_count
):
454 elif line_number
+1 == get_app().my
.gdb
.lineno
:
455 return [('class:text-area.pfx,selected', '>')]
456 elif get_app().my
.gdb
.sourcefile
in get_app().my
.gdb
.breakpoints
and line_number
+1 in get_app().my
.gdb
.breakpoints
[get_app().my
.gdb
.sourcefile
]:
457 return [('class:text-area.pfx.bp', 'o')]
459 return [('class:text-area.pfx', ' ')]
463 controls
['header'] = Label(
465 style
= u
'class:header_label',
467 controls
['codeview'] = TextArea(
473 get_line_prefix
= codeview_line_prefix
,
474 lexer
=PygmentsLexer(CLexer
),
475 style
= u
'class:codeview',
479 controls
['gdbout'] = TextArea(
484 style
= u
'class:gdbout',
485 height
= LayoutDimension(4, 16, preferred
=8),
489 controls
['inferiorout'] = TextArea(
494 style
= u
'class:inferiorout',
495 height
= LayoutDimension(1, 16, preferred
=1),
499 controls
['locals'] = sidebar('locals', lambda : get_app().my
.locals)
500 controls
['exprs'] = sidebar('exprs', lambda : get_app().my
.exprs
)
501 controls
['args'] = sidebar('args', lambda : get_app().my
.args
)
502 controls
['input_label'] = Label(
504 style
= u
'class:input_label',
505 width
= LayoutDimension
.exact(6),
507 controls
['input'] = Window(
508 content
=BufferControl(
512 history
= InMemoryHistory(),
517 height
=LayoutDimension
.exact(1),
518 dont_extend_height
=True,
519 style
= u
'class:input',
521 controls
['vardetails'] = TextArea(
522 height
=LayoutDimension(1, 4),
525 style
= u
'class:vardetails',
528 val
= get_app().my
.locals.get_value_by_index( \
529 get_app().my
.controls
['locals'].selected_option_index
)
530 text
= get_app().my
.controls
['vardetails'].text
531 if val
is None and text
!= '':
532 get_app().my
.controls
['vardetails'].text
= '<out of scope>'
534 get_app().my
.controls
['vardetails'].text
= val
[1]
535 controls
['vardetails'].update
= up_
537 def need_vardetails():
538 return get_app().my
.controls
['vardetails'].text
!= ''
540 controls
['sidebar'] = HSplit([
545 controls
['sidebar']._remaining
_space
_window
.style
= 'class:sidebar'
547 controls
['root_container'] = HSplit([
549 ConditionalContainer(controls
['vardetails'], Condition(need_vardetails
)),
552 controls
['codeview'],
553 controls
['inferiorout'],
556 controls
['input_label'],
565 get_app().exit(result
=True)
567 run_gdb_cmd(get_app(), 'c')
569 run_gdb_cmd(get_app(), 's')
571 run_gdb_cmd(get_app(), 'n')
573 if get_app().my
.focused_control
== 'codeview':
574 c
= get_app().my
.controls
['codeview']
575 line
, col
= c
.document
.translate_index_to_position(c
.document
.cursor_position
)
577 run_gdb_cmd(get_app(), 'b %s:%d'%(get_app().my
.gdb
.sourcefile
, line
))
579 def do_toggle_prompt():
580 get_app().my
.input_gdb
= not get_app().my
.input_gdb
581 get_app().my
.controls
['input_label'].text
= '(gdb) ' if get_app().my
.input_gdb
else '>>> '
582 def do_toggle_mouse():
583 # we need to have the ability to turn mouse off to use the X11
584 # clipboard (selection needs to be handled by X11, not the app)
585 get_app().my
.mouse_enabled
= not get_app().my
.mouse_enabled
587 controls
['root_container'] = MenuContainer(body
=controls
['root_container'], menu_items
=[
588 MenuItem('File', children
=[
589 MenuItem('Load config', handler
=load_config
),
590 MenuItem('Save config', handler
=save_config
),
591 MenuItem('-', disabled
=True),
592 MenuItem('Exit', handler
=do_exit
),
594 MenuItem('Debug', children
=[
595 MenuItem('Continue (F5)', handler
=do_cont
),
596 MenuItem('Step Into (F7)', handler
=do_step_into
),
597 MenuItem('Step Over (F8)', handler
=do_step_over
),
598 MenuItem('Set Breakpoint (CTRL-b)', handler
=do_set_bp
),
600 MenuItem('Extra', children
=[
601 MenuItem('Toggle python prompt (F1)', handler
=do_toggle_prompt
),
602 MenuItem('Toggle mouse support (F2)', handler
=do_toggle_mouse
),
607 @kb.add(u
'escape', 'f')
608 def _focus_menu(event
):
609 get_app().layout
.focus(get_app().my
.controls
['root_container'].window
)
619 def add_expr(name
, expr
):
620 get_app().my
.exprs_dict
[name
] = expr
622 if name
in get_app().my
.exprs_dict
:
623 del get_app().my
.exprs_dict
[name
]
624 if event
.app
.my
.focused_control
!= 'input':
625 event
.app
.my
.set_focus('input')
627 text
= event
.app
.my
.controls
['input'].content
.buffer.text
628 if len(text
) and text
[0] == ':':
630 command
, rest
= text
.split(' ', 1)
631 if command
== ':expr':
632 command
, rest
= rest
.split(' ', 1)
634 name
, expr
= rest
.split(' ', 1)
636 elif command
== 'del':
638 elif event
.app
.my
.input_gdb
:
640 if not len(cmd
): cmd
= event
.app
.my
.last_gdb_cmd
641 else: event
.app
.my
.last_gdb_cmd
= cmd
642 run_gdb_cmd(event
.app
, cmd
)
646 try: app
.my
.console
.runsource(text
)
647 except Exception as e
:
649 add_gdbview_text(event
.app
, traceback
.format_exc())
650 event
.app
.my
.controls
['input'].content
.buffer.reset(append_to_history
=True)
654 for i
in xrange(len(event
.app
.my
.focus_list
)):
655 if event
.app
.my
.focus_list
[i
] == event
.app
.my
.focused_control
:
657 if next_focus
>= len(event
.app
.my
.focus_list
):
659 event
.app
.my
.set_focus(event
.app
.my
.focus_list
[next_focus
])
665 def eff_five_(event
):
678 'gdbout':'bg:#000000 #888888',
679 'inferiorout':'bg:#330000 #888888',
680 'input' : 'bg:#000000 #8888ff underline',
681 'input_label' : 'bg:#000000 #8888ff underline',
682 'header_label' : 'bg:#9999ff #000000 underline',
683 'vardetails' : 'bg:#000000 #8888ff',
684 'text-area.pfx' : 'bg:#aaaaaa #ff0000',
685 'text-area.pfx.selected' : 'bg:#ff0000 #ffffff',
686 'sidebar': 'bg:#bbbbbb #000000',
687 'sidebar.title': 'bg:#668866 #ffffff',
688 'sidebar.title focused': 'bg:#000000 #ffffff bold',
689 'sidebar.label': 'bg:#bbbbbb #222222',
690 'sidebar.status': 'bg:#dddddd #000011',
691 'sidebar.labelodd': 'bg:#bbbb00 #222222',
692 'sidebar.statusodd': 'bg:#dddd00 #000011',
693 'sidebar.label selected': 'bg:#222222 #eeeeee',
694 'sidebar.status selected': 'bg:#444444 #ffffff bold',
695 'sidebar.status changed': 'bg:#dddddd #ff0000 bold',
696 'sidebar.statusodd changed': 'bg:#dddd00 #ff0000 bold',
699 pyg_style
= style_from_pygments_cls(CodeviewStyle
)
701 style
= merge_styles([
702 Style
.from_dict(styledict
),
707 def _is_mouse_active():
708 return get_app().my
.mouse_enabled
711 controls
['root_container'],
712 focused_element
=controls
['input'],
716 key_bindings
=merge_key_bindings([
718 load_sidebar_bindings('locals'),
719 load_inputbar_bindings(),
721 mouse_support
= _is_mouse_active
,
725 app
.my
.saved_config
= {}
726 app
.my
.mouse_enabled
= True
727 app
.my
.controls
= controls
728 app
.my
.control_to_name_mapping
= {}
729 for name
in controls
:
730 app
.my
.control_to_name_mapping
[controls
[name
]] = name
731 if isinstance(controls
[name
], TextArea
) or 'control' in vars(controls
[name
]):
732 app
.my
.control_to_name_mapping
[controls
[name
].control
] = name
733 elif 'content' in vars(controls
[name
]):
734 app
.my
.control_to_name_mapping
[controls
[name
].content
] = name
736 app
.my
.locals = OrderedDict()
737 app
.my
.args
= OrderedDict()
738 app
.my
.exprs
= OrderedDict()
739 app
.my
.exprs_dict
= dict()
741 app
.my
.last_gdb_cmd
= ''
742 app
.my
.input_gdb
= True
743 app
.my
.focus_list
= ['input', 'codeview', 'inferiorout', 'gdbout', 'args', 'locals', 'exprs']
744 app
.my
.focused_control
= 'input'
745 def _set_focus(ctrl_or_name
):
746 if isinstance(ctrl_or_name
, six
.text_type
):
747 ctrl
= get_app().my
.controls
[ctrl_or_name
]
751 name
= get_app().my
.control_to_name_mapping
[ctrl
]
752 get_app().layout
.focus(ctrl
)
753 get_app().my
.focused_control
= name
754 app
.my
.set_focus
= _set_focus
755 def _has_focus(ctrl_or_name
):
756 ctrl
= get_app().my
.controls
[ctrl_or_name
] if isinstance(ctrl_or_name
, str) else ctrl_or_name
757 return get_app().layout
.has_focus(ctrl
)
758 app
.my
.has_focus
= _has_focus
759 app_console_writefunc
= lambda x
: add_gdbview_text(get_app(), x
)
760 app
.my
.console
= py_console
.Shell(locals=globals(), writefunc
=app_console_writefunc
)
761 def my_mouse_handler(self
, mouse_event
):
762 # loosely based on prompt_toolkit/layout/controls.py:716
763 #if self.focus_on_click() and mouse_event.event_type == MouseEventType.MOUSE_DOWN:
764 if mouse_event
.event_type
== MouseEventType
.MOUSE_DOWN
:
765 get_app().my
.set_focus(self
)
766 processed_line
= self
._last
_get
_processed
_line
(mouse_event
.position
.y
)
767 xpos
= processed_line
.display_to_source(mouse_event
.position
.x
)
768 index
= self
.buffer.document
.translate_row_col_to_index(mouse_event
.position
.y
, xpos
)
769 self
.buffer.cursor_position
= index
770 else: return NotImplemented
771 for x
in app
.my
.focus_list
:
772 if isinstance(app
.my
.controls
[x
], Window
) and isinstance(app
.my
.controls
[x
].content
, SidebarControl
):
773 continue #don't override custom mouse handler
774 if isinstance(app
.my
.controls
[x
], TextArea
):
775 app
.my
.controls
[x
].control
.mouse_handler
= my_mouse_handler
.__get
__(app
.my
.controls
[x
].control
)
777 app
.my
.controls
[x
].content
.mouse_handler
= my_mouse_handler
.__get
__(app
.my
.controls
[x
].content
)
783 code
.InteractiveConsole(locals=globals()).interact()
786 return s
.replace('\t', ' ')
789 if s
== '': return False
791 if not c
in '0123456789': return False
794 def debug(app
, text
):
795 app
.my
.controls
['header'].text
= prepare_text(text
)
797 def add_gdbview_text(app
, text
):
798 pt
= prepare_text(text
)
799 ad
= '\n' + pt
.replace('\n(gdb) ', '')
800 while len(ad
) and ad
[-1] == '\n': ad
= ad
[:-1]
801 app
.my
.controls
['gdbout'].text
+= ad
802 scroll_down(app
.my
.controls
['gdbout'])
804 def codeview_set_line(ctrl
, lineno
):
805 fl
= ctrl
.window
.render_info
.first_visible_line()
806 height
= ctrl
.window
.render_info
.last_visible_line() - fl
807 direction
= -1 if lineno
< fl
else 1
808 scroll_to(ctrl
, lineno
+ (height
/2)*direction
)
810 def try_set_sourcepos(app
, file, lineno
):
811 newfile
= app
.my
.gdb
.find_sourcefile(file)
813 app
.my
.gdb
.set_sourcefile(newfile
)
814 debug(app
, app
.my
.gdb
.sourcefile
)
815 if isnumeric(lineno
): app
.my
.gdb
.lineno
= int(lineno
)
817 app
.my
.gdb
.debug("couldnt find sourcefile " + file)
820 _STEP_COMMANDS
= ['n','s','c','r','next','step','continue','run']
821 def run_gdb_cmd(app
, cmd
, hide
=False):
822 oldsrc
= app
.my
.gdb
.sourcefile
824 s
, t
= app
.my
.gdb
.read()
825 if not hide
: add_gdbview_text(app
, s
+ t
)
826 cmd0
= cmd
if not ' ' in cmd
else cmd
.split(' ')[0]
827 if cmd0
in ['finish']:
828 r
= re
.compile('(0x[0-9a-f]+) in (.+) at (.+):([0-9]+)')
829 for line
in s
.split('\n'):
832 try_set_sourcepos(app
, rm
.groups(0)[2], rm
.groups(0)[3])
834 run_gdb_cmd(app
, 's')
835 elif cmd0
in _STEP_COMMANDS
:
837 for line
in s
.split('\n'):
838 if 'Program received signal SIGSEGV' in line
: segv
=True
839 a
= line
.replace('\t', ' ').split(' ')
840 if isnumeric(a
[0]): app
.my
.gdb
.lineno
= int(a
[0])
842 file, lineno
= a
[-1].split(':')
843 if not try_set_sourcepos(app
, file, lineno
) and not segv
:
844 # we're in a file we don't have sources for, step over
845 app
.my
.gdb
.debug("run finish")
846 run_gdb_cmd(app
, 'finish')
848 if app
.my
.gdb
.istdout_canread():
849 app
.my
.controls
['inferiorout'].text
+= prepare_text(os
.read(app
.my
.gdb
.istdout(), 1024*4))
850 scroll_down(app
.my
.controls
['inferiorout'])
851 if not app
.my
.gdb
.sourcefile
== oldsrc
:
853 if not cmd
.startswith('b ') and app
.my
.gdb
.lineno
!= -1:
854 codeview_set_line(app
.my
.controls
['codeview'], app
.my
.gdb
.lineno
)
855 if cmd0
in _STEP_COMMANDS
:
859 app
.my
.controls
['vardetails'].update()
864 return "\\x" + chr(foo
).encode('hex')
866 def hexify_string_literal(s
):
871 if not escaped
and s
[i
] == '\\':
875 h
= oct_to_hex(s
[i
-1:i
+3])
876 if h
== "\\x00": break
879 else: t
+= "\\" + s
[i
]
886 def get_locals_or_args(app
, do_locals
=True):
888 app
.my
.gdb
.send('info locals')
890 app
.my
.gdb
.send('info args')
891 s
= app
.my
.gdb
.proc
.read_until(app
.my
.gdb
.proc
.stdout(), '(gdb) ')
893 for line
in s
.split('\n'):
895 k
, v
= line
.split(' = ', 1)
896 b
= tokenizer
.split_tokens(v
)
898 if mv
.startswith('times>'): mv
= b
[0]
899 if mv
[0] == "'" and len(mv
) > 3: # turn gdb's gay octal literals into hex
901 if charval
[0] == '\\' and isnumeric(charval
[1]):
902 foo
= int(charval
[1:], 8)
903 mv
= "'\\x" + chr(foo
).encode('hex') + "'"
905 mv
= hexify_string_literal(mv
)
906 elif isnumeric(mv
) and int(mv
) > 0xffff:
908 elif 'out of bounds>' in v
:
912 mylocals
[k
] = (mv
, v
)
914 app
.my
.locals.update(mylocals
)
916 app
.my
.args
.update(mylocals
)
919 return get_locals_or_args(app
, do_locals
=True)
922 return get_locals_or_args(app
, do_locals
=False)
926 for k
in app
.my
.exprs_dict
:
927 app
.my
.gdb
.send(app
.my
.exprs_dict
[k
])
928 s
= app
.my
.gdb
.proc
.read_until(app
.my
.gdb
.proc
.stdout(), '(gdb) ')
929 s
= s
.replace('\n(gdb) ', '')
930 if len(s
) > 5 and s
[0] == '$' and isnumeric(s
[1]):
932 while i
< len(s
) and isnumeric(s
[i
]): i
+= 1
933 if i
+ 2 < len(s
) and s
[i
] == ' ' and s
[i
+1] == '=' and s
[i
+2] == ' ':
934 s
= s
.split(' = ', 1)[1]
936 app
.my
.exprs
.update(mylocals
)
938 def scroll_down(control
):
939 set_lineno(control
, count_char(control
.text
, '\n')+1)
941 def scroll_to(control
, n
):
942 set_lineno(control
, n
)
944 def load_source(app
):
945 with codecs
.open(app
.my
.gdb
.sourcefile
, 'r', 'utf-8') as f
:
946 app
.my
.controls
['codeview'].text
= prepare_text(f
.read())
948 def set_lineno(control
, lineno
):
949 control
.buffer.cursor_position
= \
950 control
.buffer.document
.translate_row_col_to_index(lineno
- 1, 0)
952 def count_char(text
, ch
):
958 if __name__
== '__main__':
959 gdb
= GDB(concat_argv())
960 gdb_output
= setup_gdb(gdb
)
962 app
.my
.controls
['gdbout'].text
= prepare_text(gdb_output
)
963 scroll_down(app
.my
.controls
['gdbout'])
969 sys
.exit(gdb
.get_exitcode())