diff --git a/gdbgui/backend.py b/gdbgui/backend.py index 8e1f6203..47b07484 100755 --- a/gdbgui/backend.py +++ b/gdbgui/backend.py @@ -54,12 +54,23 @@ def run_gdb_command(): else: return client_error({'message': 'gdb is not running'}) +@app.route('/get_gdb_response') +def get_gdb_response(): + if gdb is not None: + try: + response = gdb.get_gdb_response() + return jsonify(response) + except Exception as e: + return server_error({'message': str(e)}) + else: + return client_error({'message': 'gdb is not running'}) + @app.route('/read_file') def read_file(): """Used to get contents of source files that are being debugged""" path = request.args.get('path') - if os.path.isfile(path): + if path and os.path.isfile(path): try: with open(path, 'r') as f: return jsonify({'source_code': f.read().splitlines(), @@ -85,7 +96,6 @@ def signal_handler(signal, frame): def quit_backend(): - global app, gdb gdb.exit() func = request.environ.get('werkzeug.server.shutdown') if func is None: @@ -102,7 +112,7 @@ def setup_backend(serve=True, port=5000, debug=False): app.debug = debug app.config['TEMPLATES_AUTO_RELOAD'] = True if serve: - extra_files=[] + extra_files = [] for dirname, dirs, files in os.walk(TEMPLATE_DIR): for filename in files: filename = os.path.join(dirname, filename) diff --git a/gdbgui/static/css/gdbgui.css b/gdbgui/static/css/gdbgui.css index ea5e0655..b8badf9d 100644 --- a/gdbgui/static/css/gdbgui.css +++ b/gdbgui/static/css/gdbgui.css @@ -97,3 +97,13 @@ pre{ border-width: 1px; border-radius: 2px; } +.dropdown-btn { + vertical-align: top; + height: 30px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.awesomplete ul { + overflow: auto; + max-height: 200px; +} diff --git a/gdbgui/static/js/gdbgui.js b/gdbgui/static/js/gdbgui.js index 3c267154..b58b83c2 100644 --- a/gdbgui/static/js/gdbgui.js +++ b/gdbgui/static/js/gdbgui.js @@ -2,11 +2,11 @@ "use strict"; const Util = { - get_table: function(thead, data) { + get_table: function(columns, data) { var result = [""]; result.push(""); result.push(""); - for (let h of thead){ + for (let h of columns){ result.push(``); } result.push(""); @@ -23,9 +23,27 @@ const Util = { result.push("
${h}
"); return result.join('\n'); }, + get_table_data: function(objs){ + // put keys of all objects into array + let all_keys = _.flatten(objs.map(i => _.keys(i))) + let columns = _.uniq(_.flatten(all_keys)).sort() + + let data = [] + for (let s of objs){ + let row = [] + for (let k of columns){ + row.push(k in s ? s[k] : '') + } + data.push(row) + } + return [columns, data] + }, post_msg: function(data){ - App.set_status(_.escape(data.responseJSON.message)) - // Messenger().post(_.escape(data.responseJSON.message)) + if (data.responseJSON && data.responseJSON.message){ + App.set_status(_.escape(data.responseJSON.message)) + }else{ + App.set_status(`${data.statusText} (${data.status} error)`) + } }, escape: function(s){ return s.replace(/([^\\]\\n)/g, '
') @@ -42,6 +60,8 @@ const Consts = { jq_gdb_command_input: $('#gdb_command'), jq_binary: $('#binary'), + jq_source_file_input: $('#source_file_input'), + jq_source_files_datalist: $('#source_files_datalist'), jq_code: $('#code_table'), jq_code_container: $('#code_container'), js_gdb_controls: $('.gdb_controls'), @@ -62,32 +82,36 @@ let App = { init: function(){ App.register_events(); - Consts.jq_binary.val(localStorage.getItem('last_binary')) try{ - App.state.history = JSON.parse(localStorage.getItem('history')) + App.state.past_binaries = _.uniq(JSON.parse(localStorage.getItem('past_binaries'))) + Consts.jq_binary.val(App.state.past_binaries[0]) + } catch(err){ + App.state.past_binaries = [] + } + App.render_past_binary_options_datalist() + + try{ + App.state.history = _.uniq(JSON.parse(localStorage.getItem('history'))) }catch(err){ App.state.history = [] } - if (_.isArray(App.state.history)){ - App.state.history.map(App.show_in_history_table) - } + App.render_history_table() }, onclose: function(){ - console.log(JSON.stringify(App.state.history)) - console.log(JSON.stringify(App.state.history)) - console.log(JSON.stringify(App.state.history)) - localStorage.setItem('last_binary', Consts.jq_binary.val()) - localStorage.setItem('history', JSON.stringify(App.state.history)) + localStorage.setItem('past_binaries', JSON.stringify(App.state.past_binaries) || []) + localStorage.setItem('history', JSON.stringify(App.state.history) || []) return null }, set_status: function(status){ Consts.jq_status.text(status) }, state: {'breakpoints': [], // list of breakpoints - 'source_files': [], // list of absolute paths, and their contents + 'source_files': [], // list of absolute paths + 'cached_source_files': [], // list of absolute paths, and their source code 'frame': {}, // current "frame" in gdb. Has keys: line, fullname (path to file), among others. 'rendered_source_file': {'fullname': null, 'line': null}, // current source file displayed - 'history': [] + 'history': [], + 'past_binaries': [], }, clear_state: function(){ App.state = { @@ -114,13 +138,45 @@ let App = { } ); $('.gdb_cmd').click(function(e){App.click_gdb_cmd_button(e)}); + Consts.jq_source_file_input.keyup(function(e){App.keyup_source_file_input(e)}); $('.clear_history').click(function(e){App.clear_history(e)}); $('.clear_console').click(function(e){App.clear_console(e)}); + $('.get_gdb_response').click(function(e){App.get_gdb_response(e)}); $("body").on("click", ".breakpoint", App.click_breakpoint); $("body").on("click", ".no_breakpoint", App.click_source_file_gutter_with_no_breakpoint); $("body").on("click", ".sent_command", App.click_sent_command); $("body").on("click", ".resizer", App.click_resizer_button); + Consts.jq_refresh_disassembly_button.click(App.refresh_disassembly); + App.init_autocomplete() + + + }, + init_autocomplete: function(){ + + App.autocomplete_source_file_input = new Awesomplete('#source_file_input', { + minChars: 0, + maxItems: 10000, + list: [], + sort: (a,b ) => {return a < b ? -1 : 1;} + }); + + Awesomplete.$('.dropdown-btn').addEventListener("click", function() { + if (App.autocomplete_source_file_input.ul.childNodes.length === 0) { + App.autocomplete_source_file_input.minChars = 0; + App.autocomplete_source_file_input.evaluate(); + } + else if (App.autocomplete_source_file_input.ul.hasAttribute('hidden')) { + App.autocomplete_source_file_input.open(); + } + else { + App.autocomplete_source_file_input.close(); + } + }) + + Awesomplete.$('#source_file_input').addEventListener('awesomplete-selectcomplete', function(e){ + App.read_and_render_file(e.currentTarget.value) + }); }, refresh_disassembly: function(e){ @@ -137,8 +193,13 @@ let App = { }, click_set_target_app_button: function(e){ var binary = Consts.jq_binary.val(); - App.run_gdb_command(`file ${binary}`); - App.enable_gdb_controls(); + _.remove(App.state.past_binaries, i => i === binary) + App.state.past_binaries.unshift(binary) + App.render_past_binary_options_datalist() + App.run_gdb_command(`-file-exec-and-symbols ${binary}`); + }, + render_past_binary_options_datalist: function(){ + $('#past_binaries').html(App.state.past_binaries.map(b => `