add ability to jump to line; add red error label in statusbar

This commit is contained in:
Chad Smith 2017-01-10 10:53:00 -08:00 committed by Chad Smith
parent c800218614
commit 0513235793
4 changed files with 169 additions and 101 deletions

View file

@ -28,6 +28,10 @@ pre{
.flex{
display: flex;
}
.flexrow{
display: flex;
flex-direction: row;
}
/* components get their own titlebars */
.titlebar{
width: 100%;
@ -46,43 +50,6 @@ pre{
border-width: 1px;
border-radius: 2px;
}
/* specific styling for ids */
#always_on_top{
position: fixed;
top: 0;
margin-top: 0;
height: 102px;
width: 100%;
border-bottom: black;
border-style: solid;
border-width: 0px;
border-bottom-width: 1px;
z-index: 1000;
background: #f1f1f1;
border-bottom-color: #ccc;
}
#console{
font-size: 0.9em !important;
background-color: #292929 !important;
color: #f9f9f9 !important;
font-family: monospace !important;
}
#gdb_command{
padding-left: 45px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
font-size: 0.9em !important;
background-color: #3c3c3c !important;
color: #f9f9f9 !important;
font-family: monospace !important;
}
#gdb_mi_output{
font-family: monospace;
overflow: auto;
font-size: 0.9em;
}
.sent_command:hover{
/* lighten background */
background-color:rgba(255,255,255,0.1)
@ -154,13 +121,6 @@ pre{
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* auto-complete librarie's dropdown should only be 200px high, and should
have scrollbar, so it doesn't take over the page */
.awesomplete ul {
overflow: auto;
max-height: 200px;
}
/* show a flash of color */
.flash {
-webkit-animation-name: flash-animation;
@ -180,6 +140,44 @@ have scrollbar, so it doesn't take over the page */
to { background: default; }
}
/* specific styling for ids */
#always_on_top{
position: fixed;
top: 0;
margin-top: 0;
height: 102px;
width: 100%;
border-bottom: black;
border-style: solid;
border-width: 0px;
border-bottom-width: 1px;
z-index: 1000;
background: #f1f1f1;
border-bottom-color: #ccc;
}
#console{
font-size: 0.9em !important;
background-color: #292929 !important;
color: #f9f9f9 !important;
font-family: monospace !important;
}
#gdb_command{
padding-left: 45px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
font-size: 0.9em !important;
background-color: #3c3c3c !important;
color: #f9f9f9 !important;
font-family: monospace !important;
}
#gdb_mi_output{
font-family: monospace;
overflow: auto;
font-size: 0.9em;
}
/* styling for "Variables" component */
#variables li{
list-style: none;
}
@ -194,3 +192,17 @@ have scrollbar, so it doesn't take over the page */
.toggle_children_visibility{
font-weight: bold;
}
/* Vendor Overrides */
/* awesomeplete wraps inputs in a div, and the div is really small. Make it big. */
div.awesomplete{
width: 100%;
}
/* auto-complete library (awesomplete) has unlimited dropdown size.
Limit to 200px high, and add scrollbar, so it doesn't take over the page */
.awesomplete ul {
overflow: auto;
max-height: 200px;
}

View file

@ -14,14 +14,18 @@ const ENTER_BUTTON_NUM = 13
const Status = {
el: $('#status'),
render: function(status){
Status.el.text(status)
render: function(status_str, error=false){
if(error){
Status.el.html(`<span class='label label-danger'>error</span>&nbsp;${status_str}`)
}else{
Status.el.html(status_str)
}
},
render_ajax_error_msg: function(data){
if (data.responseJSON && data.responseJSON.message){
Status.render(_.escape(data.responseJSON.message))
Status.render(_.escape(data.responseJSON.message), true)
}else{
Status.render(`${data.statusText} (${data.status} error)`)
Status.render(`${data.statusText} (${data.status} error)`, true)
}
},
render_from_gdb_mi_response: function(mi_obj){
@ -30,9 +34,14 @@ const Status = {
return
}
// Update status
let status = [];
let status = [],
error = false
if (mi_obj.message){
status.push(mi_obj.message)
if(mi_obj.message === 'error'){
error = true
}else{
status.push(mi_obj.message)
}
}
if (mi_obj.payload){
if (mi_obj.payload.msg) {status.push(mi_obj.payload.msg)}
@ -45,7 +54,7 @@ const Status = {
}
}
}
Status.render(status.join(', '))
Status.render(status.join(', '), error)
}
}
@ -368,11 +377,13 @@ const SourceCode = {
el: $('#code_table'),
el_code_container: $('#code_container'),
el_title: $('#source_code_heading'),
el_jump_to_line_input: $('#jump_to_line'),
rendered_source_file_fullname: null,
rendered_source_file_line: null,
init: function(){
$("body").on("click", ".source_code_row td .line_num", SourceCode.click_gutter)
$("body").on("click", ".view_file", SourceCode.click_view_file)
SourceCode.el_jump_to_line_input.keydown(SourceCode.keydown_jump_to_line)
},
cached_source_files: [], // list with keys fullname, source_code
click_gutter: function(e){
@ -394,7 +405,7 @@ const SourceCode = {
is_cached: function(fullname){
return SourceCode.cached_source_files.some(f => f.fullname === fullname)
},
render_source_file: function(fullname, source_code, current_line=0, options={'highlight': false, 'scroll': false}){
render_source_file: function(fullname, source_code, current_line=1, options={'highlight': false, 'scroll': false}){
current_line = parseInt(current_line)
let line_num = 1,
tbody = [],
@ -423,6 +434,7 @@ const SourceCode = {
line_num++;
}
SourceCode.el_title.text(fullname)
SourceCode.el_jump_to_line_input.val(current_line)
SourceCode.el.html(tbody.join(''))
SourceCode.rendered_source_file_fullname = fullname
@ -432,14 +444,18 @@ const SourceCode = {
SourceCode.make_current_line_visible()
}
},
make_current_line_visible: function(){
SourceCode.scroll_to_jq_selector($("#current_line"))
},
// call this to rerender a file when breakpoints change, for example
rerender: function(){
if(_.isString(SourceCode.rendered_source_file_fullname)){
// TODO redraw only breakpoint rows, not the whole source file
SourceCode.fetch_and_render_file(SourceCode.rendered_source_file_fullname, SourceCode.rendered_source_file_line, {'highlight': false, 'scroll': false})
}
},
// fetch file and render it, or used cached file if we have it
fetch_and_render_file: function(fullname, current_line=0, options={'highlight': false, 'scroll': false}){
fetch_and_render_file: function(fullname, current_line=1, options={'highlight': false, 'scroll': false}){
if (!_.isString(fullname)){
console.error('cannot render file without a name')
@ -467,22 +483,25 @@ const SourceCode = {
})
}
},
make_current_line_visible: function(){
const time_to_scroll = 0
let jq_current_line = $("#current_line")
if (jq_current_line.length === 1){ // make sure a line is selected before trying to scroll to it
/**
* Scroll to a jQuery selection in the source code table
* Used to jump around to various lines
*/
scroll_to_jq_selector: function(jq_selector){
if (jq_selector.length === 1){ // make sure a line is selected before trying to scroll to it
let top_of_container = SourceCode.el_code_container.position().top,
height_of_container = SourceCode.el_code_container.height(),
bottom_of_container = top_of_container + height_of_container,
top_of_line = jq_current_line.position().top,
bottom_of_line = top_of_line+ jq_current_line.height(),
top_of_table = jq_current_line.closest('table').position().top
top_of_line = jq_selector.position().top,
bottom_of_line = top_of_line+ jq_selector.height(),
top_of_table = jq_selector.closest('table').position().top
if ((top_of_line >= top_of_container) && (bottom_of_line < (bottom_of_container))){
// do nothing, it's already in view
}else{
// line is out of view, scroll so it's in the middle of the table
SourceCode.el_code_container.animate({'scrollTop': top_of_line - (top_of_table + height_of_container/2)}, 0)
const time_to_scroll = 0
SourceCode.el_code_container.animate({'scrollTop': top_of_line - (top_of_table + height_of_container/2)}, time_to_scroll)
}
}else{
@ -514,6 +533,16 @@ const SourceCode = {
line = e.currentTarget.dataset['line'],
highlight = e.currentTarget.dataset['highlight'] === 'true'
SourceCode.fetch_and_render_file(fullname, line, {'highlight': highlight, 'scroll': true})
},
keydown_jump_to_line: function(e){
if (e.keyCode === ENTER_BUTTON_NUM){
let line = e.currentTarget.value
SourceCode.jump_to_line(line)
}
},
jump_to_line: function(line){
let jq_selector = $(`.line_num[data-line=${line}]`)
SourceCode.scroll_to_jq_selector(jq_selector)
}
}
@ -551,14 +580,31 @@ const SourceFileAutocomplete = {
// perform action when an item is selected
Awesomplete.$('#source_file_input').addEventListener('awesomplete-selectcomplete', function(e){
SourceCode.fetch_and_render_file(e.currentTarget.value)
let fullname = e.currentTarget.value,
line = 1
SourceCode.fetch_and_render_file(fullname, 1, {'highlight': false, 'scroll': true})
})
},
keyup_source_file_input: function(e){
if (e.keyCode === ENTER_BUTTON_NUM){
SourceCode.fetch_and_render_file(e.currentTarget.value)
let user_input = _.trim(e.currentTarget.value)
if(user_input.length === 0){
return
}
// if user enterted "/path/to/file.c:line", be friendly and parse out the line for them
let user_input_array = user_input.split(':'),
file = user_input_array[0],
line = 1
if(user_input_array.length === 2){
line = user_input_array[1]
}
SourceCode.fetch_and_render_file(file, line, {'highlight': false, 'scroll': true})
}
},
}
}
/**

View file

@ -28,7 +28,7 @@ body
form(role=form)
div.input-group.input-group-sm
span.input-group-btn
button.btn.btn-primary#set_target_app(type="button") Load This Binary
button.btn.btn-primary#set_target_app(type="button") Load This Binary and Args
input.form-control#binary(type="text", placeholder="/path/to/target/executable -and -flags", list='past_binaries', style="font-family: courier")
datalist#past_binaries
@ -63,26 +63,31 @@ body
//- source
div.no_padding.col-md-12#source_div
div.titlebar.flex
span#source_code_heading.lighttext (no source code file loaded)
input#jump_to_line.form-control.dropdown-input(autocomplete='on', placeholder='line', title='Enter line number, then press enter', style="width: 150px; position: inline")
div.titlebar
div
input#source_file_input.dropdown-input(autocomplete='off', placeholder='Enter source file path here, or select from dropdown', style="width: 500px")
div.flexrow
input#source_file_input.form-control.dropdown-input(autocomplete='off', placeholder='Enter source file path to view, or load binary then populate and select from dropdown', style='flex:1')
button.dropdown-btn(type="button")
span.caret
button.dropdown-btn(type="button")
span.caret
button.btn.btn-default.gdb_cmd.btn-xs(type="button", data-cmd='-file-list-exec-source-files') Populate List
button.btn.btn-default.gdb_cmd.btn-xs(type="button", data-cmd='-file-list-exec-source-files') Populate List
div.flex
//- display loaded file path and input to jump to line
span#source_code_heading.lighttext (no source code file loaded)
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#code_container" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#code_container" data-resize_type="shrink")
//- input to jump to a line in the source code
input#jump_to_line.form-control.dropdown-input(autocomplete='on', placeholder='jump to line', title='Enter line number, then press enter', style="width: 150px; height: 25px; margin-left: 10")
div#code_container.gdb_content_div(style="height: 300px")
table#code_table.code
tr
td Source code will be displayed here when applicable
div.col-md-6.no_padding
div.col-md-12.no_padding
div.titlebar
span.lighttext stack
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#stack" data-resize_type="enlarge")
@ -91,24 +96,24 @@ body
span.glyphicon.glyphicon-refresh.padding_left
div#stack.gdb_content_div
div.col-md-6.no_padding
div.col-md-12.no_padding
div.titlebar
span.lighttext breakpoints
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd='-break-list', title="Get table of all breakpoints (-break-list')") Refresh
span.glyphicon.glyphicon-refresh.padding_left
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#breakpoints" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#breakpoints" data-resize_type="shrink")
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd='-break-list', title="Get table of all breakpoints (-break-list')") Refresh
span.glyphicon.glyphicon-refresh.padding_left
div#breakpoints.gdb_content_div
div.col-md-6.no_padding
div.col-md-12.no_padding
div.titlebar.flex
span.lighttext variables
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#variables" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#variables" data-resize_type="shrink")
input#variable_input.form-control(placeholder='reach breakpoint, then enter expression or variable and press enter', style="height:20px")
input#variable_input.form-control(placeholder='reach breakpoint, then enter expression or variable and press enter', style="height:25px")
div#variables.gdb_content_div
div.col-md-6.no_padding
div.col-md-2.no_padding
div.titlebar
span.lighttext registers
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#registers" data-resize_type="enlarge")
@ -116,9 +121,16 @@ body
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd0='-data-list-register-names', data-cmd1='-data-list-register-values x', title='Refresh all register names and values (-data-list-register-values x)') Refresh
span.glyphicon.glyphicon-refresh.padding_left
div#registers.gdb_content_div
div.col-md-6.no_padding
div.col-md-10.no_padding
div.titlebar
span.lighttext memory
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#memory" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#memory" data-resize_type="shrink")
div#memory.gdb_content_div
div.row
div.col-md-6.no_padding
div.titlebar.flex
span.lighttext disassembly
span#disassembly_heading
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#disassembly" data-resize_type="enlarge")
@ -127,32 +139,25 @@ body
span.glyphicon.glyphicon-refresh.padding_left
div#disassembly.gdb_content_div
div.col-md-6.no_padding
div.titlebar
span.lighttext memory
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#memory" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#memory" data-resize_type="shrink")
div#memory.gdb_content_div
div.col-md-6.no_padding
div.titlebar
span.lighttext All Local Variables
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd='-stack-list-locals --all-values', title="Refresh all data names and values (-stack-list-locals --all-values')") Refresh
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#all_local_variables" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#all_local_variables" data-resize_type="shrink")
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd='-stack-list-locals --all-values', title="Refresh all data names and values (-stack-list-locals --all-values')") Refresh
div#all_local_variables.gdb_content_div
div.col-md-12.no_padding
//- text/buttons above the gdb console output
div.titlebar
span.lighttext gdb console
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#console" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#console" data-resize_type="shrink")
div.btn-group
button.btn.btn-default.btn-xs.clear_console(type='button', title='Clear Console (clears ui element, does not send a gdb command)') Clear Console
span.glyphicon.glyphicon-ban-circle.padding_left
button.btn.btn-default.btn-xs.gdb_cmd(type='button', data-cmd='-data-evaluate-expression fflush(0)', title='Flush output buffers by evaluating fflush(0) call (-data-evaluate-expression fflush(0))') Flush Output
span.glyphicon.glyphicon-download.padding_left
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#console" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#console" data-resize_type="shrink")
//- gdb console output
div#console.gdb_content_div(style='border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0')
@ -161,16 +166,22 @@ body
span(style="position: absolute; left: 5px; margin-top: 9px; color: #777; font-family: monospace; font-size: 0.9em") (gdb)
input.form-control.dropdown-input#gdb_command(type="text", autocomplete="on", data-list="#gdb_command_reference", placeholder='enter any gdb command, or gdb machine interface command')
div.col-md-6.no_padding
div.titlebar
span.lighttext gdb machine interface output
span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#gdb_mi_output" data-resize_type="enlarge")
span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#gdb_mi_output" data-resize_type="shrink")
button.btn.btn-default.btn-xs.clear_mi_output(type='button', title='Clear (clears ui element, does not send a gdb command)') Clear
span.glyphicon.glyphicon-ban-circle.padding_left
div#gdb_mi_output.gdb_content_div
//- Uncomment to assist when developing/debugging gdbgui
//- div.col-md-6.no_padding
//- div.titlebar
//- span.lighttext gdb machine interface output
//- span.glyphicon.glyphicon-plus.resizer.pointer(data-target_selector="#gdb_mi_output" data-resize_type="enlarge")
//- span.glyphicon.glyphicon-minus.resizer.pointer(data-target_selector="#gdb_mi_output" data-resize_type="shrink")
//- button.btn.btn-default.btn-xs.clear_mi_output(type='button', title='Clear (clears ui element, does not send a gdb command)') Clear
//- span.glyphicon.glyphicon-ban-circle.padding_left
//- div#gdb_mi_output.gdb_content_div
footer(style='height:200px; width:100%; margin:0; margin-top: 50px; padding:20px; background-color: #f5f5f5')
span made by
=' '
a(href='https://linkedin.com/in/chadsmith27', target='_blank') Chad Smith
=' | '
a(href='http://grasssfedcom.com', target='_blank') grassfedcode.com
div.row
iframe(src="https://ghbtns.com/github-btn.html?user=cs01&repo=gdbgui&type=star&count=false", frameborder="0", scrolling="0", width="70px", height="20px")
br

View file

@ -1,7 +1,6 @@
from setuptools import find_packages, setup, Command
import sys
# from distutils.core import setup,
EXCLUDE_FROM_PACKAGES = []
version = '0.5'
@ -45,7 +44,7 @@ setup(
cmdclass={'test': TestCommand},
install_requires=[
'Flask>=0.11.1',
'pygdbmi>=0.0.1.9',
'pygdbmi>=0.6',
'pyjade>=4.0.0'
],
classifiers=[