Skip to content

Commit 5dbc937

Browse files
authored
Merge pull request #1 from cs01/dev/cs01/general-improvements
2 parents 16af262 + 6eb750a commit 5dbc937

File tree

8 files changed

+851
-136
lines changed

8 files changed

+851
-136
lines changed

gdbgui/backend.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,23 @@ def run_gdb_command():
5454
else:
5555
return client_error({'message': 'gdb is not running'})
5656

57+
@app.route('/get_gdb_response')
58+
def get_gdb_response():
59+
if gdb is not None:
60+
try:
61+
response = gdb.get_gdb_response()
62+
return jsonify(response)
63+
except Exception as e:
64+
return server_error({'message': str(e)})
65+
else:
66+
return client_error({'message': 'gdb is not running'})
67+
5768

5869
@app.route('/read_file')
5970
def read_file():
6071
"""Used to get contents of source files that are being debugged"""
6172
path = request.args.get('path')
62-
if os.path.isfile(path):
73+
if path and os.path.isfile(path):
6374
try:
6475
with open(path, 'r') as f:
6576
return jsonify({'source_code': f.read().splitlines(),
@@ -85,7 +96,6 @@ def signal_handler(signal, frame):
8596

8697

8798
def quit_backend():
88-
global app, gdb
8999
gdb.exit()
90100
func = request.environ.get('werkzeug.server.shutdown')
91101
if func is None:
@@ -102,7 +112,7 @@ def setup_backend(serve=True, port=5000, debug=False):
102112
app.debug = debug
103113
app.config['TEMPLATES_AUTO_RELOAD'] = True
104114
if serve:
105-
extra_files=[]
115+
extra_files = []
106116
for dirname, dirs, files in os.walk(TEMPLATE_DIR):
107117
for filename in files:
108118
filename = os.path.join(dirname, filename)

gdbgui/static/css/gdbgui.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,13 @@ pre{
9797
border-width: 1px;
9898
border-radius: 2px;
9999
}
100+
.dropdown-btn {
101+
vertical-align: top;
102+
height: 30px;
103+
border-top-left-radius: 0;
104+
border-bottom-left-radius: 0;
105+
}
106+
.awesomplete ul {
107+
overflow: auto;
108+
max-height: 200px;
109+
}

gdbgui/static/js/gdbgui.js

Lines changed: 128 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"use strict";
33

44
const Util = {
5-
get_table: function(thead, data) {
5+
get_table: function(columns, data) {
66
var result = ["<table class='table table-striped table-bordered table-condensed'>"];
77
result.push("<thead>");
88
result.push("<tr>");
9-
for (let h of thead){
9+
for (let h of columns){
1010
result.push(`<th>${h}</th>`);
1111
}
1212
result.push("</tr>");
@@ -23,9 +23,27 @@ const Util = {
2323
result.push("</table>");
2424
return result.join('\n');
2525
},
26+
get_table_data: function(objs){
27+
// put keys of all objects into array
28+
let all_keys = _.flatten(objs.map(i => _.keys(i)))
29+
let columns = _.uniq(_.flatten(all_keys)).sort()
30+
31+
let data = []
32+
for (let s of objs){
33+
let row = []
34+
for (let k of columns){
35+
row.push(k in s ? s[k] : '')
36+
}
37+
data.push(row)
38+
}
39+
return [columns, data]
40+
},
2641
post_msg: function(data){
27-
App.set_status(_.escape(data.responseJSON.message))
28-
// Messenger().post(_.escape(data.responseJSON.message))
42+
if (data.responseJSON && data.responseJSON.message){
43+
App.set_status(_.escape(data.responseJSON.message))
44+
}else{
45+
App.set_status(`${data.statusText} (${data.status} error)`)
46+
}
2947
},
3048
escape: function(s){
3149
return s.replace(/([^\\]\\n)/g, '<br>')
@@ -42,6 +60,8 @@ const Consts = {
4260

4361
jq_gdb_command_input: $('#gdb_command'),
4462
jq_binary: $('#binary'),
63+
jq_source_file_input: $('#source_file_input'),
64+
jq_source_files_datalist: $('#source_files_datalist'),
4565
jq_code: $('#code_table'),
4666
jq_code_container: $('#code_container'),
4767
js_gdb_controls: $('.gdb_controls'),
@@ -62,32 +82,36 @@ let App = {
6282
init: function(){
6383
App.register_events();
6484

65-
Consts.jq_binary.val(localStorage.getItem('last_binary'))
6685
try{
67-
App.state.history = JSON.parse(localStorage.getItem('history'))
86+
App.state.past_binaries = _.uniq(JSON.parse(localStorage.getItem('past_binaries')))
87+
Consts.jq_binary.val(App.state.past_binaries[0])
88+
} catch(err){
89+
App.state.past_binaries = []
90+
}
91+
App.render_past_binary_options_datalist()
92+
93+
try{
94+
App.state.history = _.uniq(JSON.parse(localStorage.getItem('history')))
6895
}catch(err){
6996
App.state.history = []
7097
}
71-
if (_.isArray(App.state.history)){
72-
App.state.history.map(App.show_in_history_table)
73-
}
98+
App.render_history_table()
7499
},
75100
onclose: function(){
76-
console.log(JSON.stringify(App.state.history))
77-
console.log(JSON.stringify(App.state.history))
78-
console.log(JSON.stringify(App.state.history))
79-
localStorage.setItem('last_binary', Consts.jq_binary.val())
80-
localStorage.setItem('history', JSON.stringify(App.state.history))
101+
localStorage.setItem('past_binaries', JSON.stringify(App.state.past_binaries) || [])
102+
localStorage.setItem('history', JSON.stringify(App.state.history) || [])
81103
return null
82104
},
83105
set_status: function(status){
84106
Consts.jq_status.text(status)
85107
},
86108
state: {'breakpoints': [], // list of breakpoints
87-
'source_files': [], // list of absolute paths, and their contents
109+
'source_files': [], // list of absolute paths
110+
'cached_source_files': [], // list of absolute paths, and their source code
88111
'frame': {}, // current "frame" in gdb. Has keys: line, fullname (path to file), among others.
89112
'rendered_source_file': {'fullname': null, 'line': null}, // current source file displayed
90-
'history': []
113+
'history': [],
114+
'past_binaries': [],
91115
},
92116
clear_state: function(){
93117
App.state = {
@@ -114,13 +138,45 @@ let App = {
114138
}
115139
);
116140
$('.gdb_cmd').click(function(e){App.click_gdb_cmd_button(e)});
141+
Consts.jq_source_file_input.keyup(function(e){App.keyup_source_file_input(e)});
117142
$('.clear_history').click(function(e){App.clear_history(e)});
118143
$('.clear_console').click(function(e){App.clear_console(e)});
144+
$('.get_gdb_response').click(function(e){App.get_gdb_response(e)});
119145
$("body").on("click", ".breakpoint", App.click_breakpoint);
120146
$("body").on("click", ".no_breakpoint", App.click_source_file_gutter_with_no_breakpoint);
121147
$("body").on("click", ".sent_command", App.click_sent_command);
122148
$("body").on("click", ".resizer", App.click_resizer_button);
149+
123150
Consts.jq_refresh_disassembly_button.click(App.refresh_disassembly);
151+
App.init_autocomplete()
152+
153+
154+
},
155+
init_autocomplete: function(){
156+
157+
App.autocomplete_source_file_input = new Awesomplete('#source_file_input', {
158+
minChars: 0,
159+
maxItems: 10000,
160+
list: [],
161+
sort: (a,b ) => {return a < b ? -1 : 1;}
162+
});
163+
164+
Awesomplete.$('.dropdown-btn').addEventListener("click", function() {
165+
if (App.autocomplete_source_file_input.ul.childNodes.length === 0) {
166+
App.autocomplete_source_file_input.minChars = 0;
167+
App.autocomplete_source_file_input.evaluate();
168+
}
169+
else if (App.autocomplete_source_file_input.ul.hasAttribute('hidden')) {
170+
App.autocomplete_source_file_input.open();
171+
}
172+
else {
173+
App.autocomplete_source_file_input.close();
174+
}
175+
})
176+
177+
Awesomplete.$('#source_file_input').addEventListener('awesomplete-selectcomplete', function(e){
178+
App.read_and_render_file(e.currentTarget.value)
179+
});
124180

125181
},
126182
refresh_disassembly: function(e){
@@ -137,8 +193,13 @@ let App = {
137193
},
138194
click_set_target_app_button: function(e){
139195
var binary = Consts.jq_binary.val();
140-
App.run_gdb_command(`file ${binary}`);
141-
App.enable_gdb_controls();
196+
_.remove(App.state.past_binaries, i => i === binary)
197+
App.state.past_binaries.unshift(binary)
198+
App.render_past_binary_options_datalist()
199+
App.run_gdb_command(`-file-exec-and-symbols ${binary}`);
200+
},
201+
render_past_binary_options_datalist: function(){
202+
$('#past_binaries').html(App.state.past_binaries.map(b => `<option>${b}</option`))
142203
},
143204
keydown_on_binary_input: function(e){
144205
if(e.keyCode === 13) {
@@ -163,7 +224,6 @@ let App = {
163224
}
164225
},
165226
click_resizer_button: function(e){
166-
console.log(e)
167227
let jq_selection = $(e.currentTarget.dataset['target_selector'])
168228
let cur_height = jq_selection.height()
169229
if (e.currentTarget.dataset['resize_type'] === 'enlarge'){
@@ -205,9 +265,9 @@ let App = {
205265
return
206266
}
207267

208-
App.set_status('')
268+
App.set_status(`running command "${cmd}"`)
209269
App.save_to_history(cmd)
210-
App.show_in_history_table(cmd)
270+
App.render_history_table()
211271
$.ajax({
212272
url: "/run_gdb_command",
213273
cache: false,
@@ -217,6 +277,15 @@ let App = {
217277
error: Util.post_msg
218278
})
219279
},
280+
get_gdb_response: function(){
281+
App.set_status(`Getting GDB response`)
282+
$.ajax({
283+
url: "/get_gdb_response",
284+
cache: false,
285+
success: App.receive_gdb_response,
286+
error: Util.post_msg
287+
})
288+
},
220289
clear_history: function(){
221290
App.state.history = []
222291
Consts.jq_command_history.html('')
@@ -226,13 +295,15 @@ let App = {
226295
},
227296
save_to_history: function(cmd){
228297
if (_.isArray(App.state.history)){
229-
App.state.history.push(cmd)
298+
_.remove(App.state.history, i => i === cmd)
299+
App.state.history.unshift(cmd)
230300
}else{
231301
App.state.history = [cmd]
232302
}
233303
},
234-
show_in_history_table: function(cmd){
235-
Consts.jq_command_history.prepend(`<tr><td class="sent_command pointer" data-cmd="${cmd}" style="padding: 0">${cmd}</td></tr>`)
304+
render_history_table: function(){
305+
let history_html = App.state.history.map(cmd => `<tr><td class="sent_command pointer" data-cmd="${cmd}" style="padding: 0">${cmd}</td></tr>`)
306+
Consts.jq_command_history.html(history_html)
236307
},
237308
receive_gdb_response: function(response_array){
238309
const text_class = {
@@ -274,14 +345,21 @@ let App = {
274345
App.render_cached_source_file();
275346
} else if ('stack' in r.payload) {
276347
App.render_stack(r.payload.stack)
348+
277349
} else if ('register-values' in r.payload) {
278350
if (App.register_names){
279351
App.render_registers(App.register_names, r.payload['register-values'])
280352
}
281353
} else if ('register-names' in r.payload) {
282354
App.register_names = r.payload['register-names']
355+
283356
} else if ('asm_insns' in r.payload) {
284357
App.render_disasembly(r.payload.asm_insns)
358+
359+
} else if ('files' in r.payload){
360+
App.source_files = _.uniq(r.payload.files.map(f => f.fullname)).sort()
361+
App.autocomplete_source_file_input.list = App.source_files
362+
App.autocomplete_source_file_input.evaluate()
285363
}
286364

287365
} else if (r.payload && typeof r.payload.frame !== 'undefined') {
@@ -307,20 +385,30 @@ let App = {
307385
if (r.message){
308386
status.push(r.message)
309387
}
310-
if (r.payload && r.payload.msg){
311-
status.push(r.payload.msg)
312-
}
313-
if (r.payload && r.payload.reason){
314-
status.push(r.payload.reason)
388+
if (r.payload){
389+
if (r.payload.msg) {status.push(r.payload.msg)}
390+
if (r.payload.reason) {status.push(r.payload.reason)}
391+
if (r.payload.frame){
392+
for(let i of ['file', 'func', 'line']){
393+
if (i in r.payload.frame){
394+
status.push(`${i}: ${r.payload.frame[i]}`)
395+
}
396+
}
397+
}
315398
}
316-
App.set_status(status.join('. '))
399+
App.set_status(status.join(', '))
317400
}
318401

319402
// scroll to the bottom
320403
Consts.jq_stdout.animate({'scrollTop': Consts.jq_stdout.prop('scrollHeight')})
321404
Consts.jq_gdb_mi_output.animate({'scrollTop': Consts.jq_gdb_mi_output.prop('scrollHeight')})
322405
Consts.jq_console.animate({'scrollTop': Consts.jq_console.prop('scrollHeight')})
323406
},
407+
keyup_source_file_input: function(e){
408+
if (e.keyCode === 13){
409+
App.read_and_render_file(e.currentTarget.value)
410+
}
411+
},
324412
render_disasembly: function(asm_insns){
325413
let thead = [ 'line', 'function+offset address instruction']
326414
let data = []
@@ -332,23 +420,22 @@ let App = {
332420
Consts.jq_disassembly_heading.html(asm_insns['fullname'])
333421
},
334422
render_stack: function(stack){
335-
let thead = _.keys(stack[0])
336-
let stack_array = stack.map(b => _.values(b));
337-
Consts.jq_stack.html(Util.get_table(thead, stack_array));
423+
let [columns, data] = Util.get_table_data(stack)
424+
Consts.jq_stack.html(Util.get_table(columns, data));
338425
},
339426
render_registers(names, values){
340-
let thead = ['name', 'value']
427+
let columns = ['name', 'value']
341428
let register_array = values.map(v => [names[v['number']], v['value']]);
342-
Consts.jq_registers.html(Util.get_table(thead, register_array));
429+
Consts.jq_registers.html(Util.get_table(columns, register_array));
343430
},
344431
read_and_render_file: function(fullname, highlight_line=0){
345-
if (fullname === null){
432+
if (fullname === null || fullname === undefined){
346433
return
347434
}
348435

349-
if (App.state.source_files.some(f => f.fullname === fullname)){
436+
if (App.state.cached_source_files.some(f => f.fullname === fullname)){
350437
// We have this cached locally, just use it!
351-
let f = _.find(App.state.source_files, i => i.fullname === fullname);
438+
let f = _.find(App.state.cached_source_files, i => i.fullname === fullname);
352439
App.render_source_file(fullname, f.source_code, highlight_line);
353440
return
354441
}
@@ -359,7 +446,7 @@ let App = {
359446
type: 'GET',
360447
data: {path: fullname},
361448
success: function(response){
362-
App.state.source_files.push({'fullname': fullname, 'source_code': response.source_code})
449+
App.state.cached_source_files.push({'fullname': fullname, 'source_code': response.source_code})
363450
App.render_source_file(fullname, response.source_code, highlight_line);
364451
},
365452
error: Util.post_msg
@@ -407,9 +494,8 @@ let App = {
407494
App.state.breakpoints.push(breakpoint);
408495
},
409496
render_breakpoint_table: function(){
410-
const thead = _.keys(App.state.breakpoints[0]);
411-
let bkpt_array = App.state.breakpoints.map(b => _.values(b));
412-
Consts.jq_breakpoints.html(Util.get_table(thead, bkpt_array));
497+
let [columns, data] = Util.get_table_data(App.state.breakpoints)
498+
Consts.jq_breakpoints.html(Util.get_table(columns, data))
413499
},
414500
enable_gdb_controls: function(){
415501
Consts.js_gdb_controls.removeClass('disabled');

0 commit comments

Comments
 (0)