├── .gitignore ├── .no-sublime-package ├── Context.sublime-menu ├── Default.sublime-commands ├── Default.sublime-keymap ├── Default.sublime-mousemap ├── LICENSE ├── Main.sublime-menu ├── Readme.creole ├── SublimeGDB.sublime-settings ├── gdb.tmLanguage ├── gdb_disasm.tmLanguage ├── gdb_registers.tmLanguage ├── gdb_session.tmLanguage ├── resultparser.py └── sublimegdb.py /.gitignore: -------------------------------------------------------------------------------- 1 | sublimegdb.sublime-project 2 | sublimegdb.sublime-workspace 3 | *.pyc 4 | -------------------------------------------------------------------------------- /.no-sublime-package: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarnster/SublimeGDB/fcd83adef823885874a84bab184e086c94f689cc/.no-sublime-package -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "gdb_menu", 3 | "caption": "GDB", 4 | "children": [ 5 | { "caption": "-", "id": "breakpoints" }, 6 | { "command": "gdb_toggle_breakpoint", "caption": "Toggle Breakpoint" }, 7 | { "command": "gdb_launch", "caption": "Run"}, 8 | { "command": "gdb_load", "caption": "Load"}, 9 | { "command": "gdb_pause", "caption": "Pause"}, 10 | { "command": "gdb_continue", "caption": "Continue" }, 11 | { "command": "gdb_step_over", "caption": "Step Over" }, 12 | { "command": "gdb_step_into", "caption": "Step Into" }, 13 | { "command": "gdb_step_out", "caption": "Step Out" }, 14 | { 15 | "caption": "SublimeGDB: Step Next Instruction", 16 | "command": "gdb_next_instruction" 17 | }, 18 | { "command": "gdb_exit", "caption": "Stop Debugging"}, 19 | { "caption": "-", "id": "gdb_views" }, 20 | { 21 | "caption": "Open Breakpoint View", 22 | "command": "gdb_open_breakpoint_view" 23 | }, 24 | { 25 | "caption": "Open Callstack View", 26 | "command": "gdb_open_callstack_view" 27 | }, 28 | { 29 | "caption": "Open Console View", 30 | "command": "gdb_open_console_view" 31 | }, 32 | { 33 | "caption": "Open Disassembly View", 34 | "command": "gdb_open_disassembly_view" 35 | }, 36 | { 37 | "caption": "Open Register View", 38 | "command": "gdb_open_register_view" 39 | }, 40 | { 41 | "caption": "Open Session View", 42 | "command": "gdb_open_session_view" 43 | }, 44 | { 45 | "caption": "Open Threads View", 46 | "command": "gdb_open_threads_view" 47 | }, 48 | { 49 | "caption": "Open Variables View", 50 | "command": "gdb_open_variables_view" 51 | }, 52 | { 53 | "caption": "Add watch", 54 | "command": "gdb_add_watch" 55 | } 56 | ] 57 | }] 58 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences: SublimeGDB Settings – Default", 4 | "command": "open_file", "args": 5 | { 6 | "file": "${packages}/SublimeGDB/SublimeGDB.sublime-settings" 7 | } 8 | }, 9 | { 10 | "caption": "Preferences: SublimeGDB Settings – User", 11 | "command": "open_file", "args": 12 | { 13 | "file": "${packages}/User/SublimeGDB.sublime-settings" 14 | } 15 | }, 16 | { 17 | "caption": "SublimeGDB: Show Input Field", 18 | "command": "gdb_input" 19 | }, 20 | { 21 | "caption": "SublimeGDB: Show Raw Input Field", 22 | "command": "gdb_raw_input" 23 | }, 24 | { 25 | "caption": "SublimeGDB: Start Debugging", 26 | "command": "gdb_launch" 27 | }, 28 | { 29 | "caption": "SublimeGDB: Continue", 30 | "command": "gdb_continue" 31 | }, 32 | { 33 | "caption": "SublimeGDB: Pause", 34 | "command": "gdb_pause" 35 | }, 36 | { 37 | "caption": "SublimeGDB: Step Over", 38 | "command": "gdb_step_over" 39 | }, 40 | { 41 | "caption": "SublimeGDB: Step Into", 42 | "command": "gdb_step_into" 43 | }, 44 | { 45 | "caption": "SublimeGDB: Step Next Instruction", 46 | "command": "gdb_next_instruction" 47 | }, 48 | { 49 | "caption": "SublimeGDB: Step Out", 50 | "command": "gdb_step_out" 51 | }, 52 | { 53 | "caption": "SublimeGDB: Stop Debugging", 54 | "command": "gdb_exit" 55 | }, 56 | { 57 | "caption": "SublimeGDB: Open Register View", 58 | "command": "gdb_open_register_view" 59 | }, 60 | { 61 | "caption": "SublimeGDB: Open Session View", 62 | "command": "gdb_open_session_view" 63 | }, 64 | { 65 | "caption": "SublimeGDB: Open Console View", 66 | "command": "gdb_open_console_view" 67 | }, 68 | { 69 | "caption": "SublimeGDB: Open Callstack View", 70 | "command": "gdb_open_callstack_view" 71 | }, 72 | { 73 | "caption": "SublimeGDB: Open Variables View", 74 | "command": "gdb_open_variables_view" 75 | }, 76 | { 77 | "caption": "SublimeGDB: Open Disassembly View", 78 | "command": "gdb_open_disassembly_view" 79 | }, 80 | { 81 | "caption": "SublimeGDB: Open Breakpoint View", 82 | "command": "gdb_open_breakpoint_view" 83 | }, 84 | { 85 | "caption": "SublimeGDB: Open Threads View", 86 | "command": "gdb_open_threads_view" 87 | } 88 | ] 89 | -------------------------------------------------------------------------------- /Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | { 4 | "command": "gdb_edit_register", 5 | "keys": ["enter"], 6 | "context": [{"key": "gdb_running"}, {"key": "gdb_register_view"}] 7 | }, 8 | { 9 | "command": "gdb_edit_variable", 10 | "keys": ["enter"], 11 | "context": [{"key": "gdb_running"}, {"key": "gdb_variables_view"}] 12 | }, 13 | { 14 | "command": "gdb_expand_variable", 15 | "keys": ["right"], 16 | "context": [{"key": "gdb_running"}, {"key": "gdb_variables_view"}] 17 | }, 18 | { 19 | "command": "gdb_collapse_variable", 20 | "keys": ["left"], 21 | "context": [{"key": "gdb_running"}, {"key": "gdb_variables_view"}] 22 | }, 23 | { 24 | "command": "gdb_input", 25 | "keys": ["shift+f5"] 26 | }, 27 | { 28 | "command": "gdb_raw_input", 29 | "keys": ["ctrl+shift+f5"] 30 | }, 31 | { 32 | "command": "gdb_toggle_breakpoint", 33 | "keys": ["f9"] 34 | }, 35 | { 36 | "command": "gdb_launch", 37 | "context": [{"key": "gdb_running", "operator": "equal", "operand": false}], 38 | "keys": ["f5"] 39 | }, 40 | { 41 | "command": "gdb_exit", 42 | "context": [{"key": "gdb_running", "operator": "equal", "operand": true}], 43 | "keys": ["ctrl+f5"] 44 | }, 45 | { 46 | "command": "gdb_continue", 47 | "context": [{"key": "gdb_running", "operator": "equal", "operand": true}], 48 | "keys": ["f5"] 49 | }, 50 | { 51 | "command": "gdb_step_over", 52 | "context": 53 | [ 54 | {"key": "gdb_running", "operator": "equal", "operand": true}, 55 | {"key": "gdb_disassembly_view", "operand": false} 56 | ], 57 | "keys": ["f10"] 58 | }, 59 | { 60 | "command": "gdb_next_instruction", 61 | "context": 62 | [ 63 | {"key": "gdb_running", "operator": "equal", "operand": true}, 64 | {"key": "gdb_disassembly_view", "operand": true} 65 | ], 66 | "keys": ["f10"] 67 | }, 68 | { 69 | "command": "gdb_step_into", 70 | "context": [{"key": "gdb_running", "operator": "equal", "operand": true}], 71 | "keys": ["f11"] 72 | }, 73 | { 74 | "command": "gdb_step_out", 75 | "context": [{"key": "gdb_running", "operator": "equal", "operand": true}], 76 | "keys": ["shift+f11"] 77 | }, 78 | { 79 | "command": "gdb_open_register_view", 80 | "context": [{"key": "gdb_register_view_open", "operator": "equal", "operand": false }], 81 | "keys": ["ctrl+alt+g"] 82 | }, 83 | { 84 | "command": "gdb_open_disassembly_view", 85 | "context": [{"key": "gdb_disassembly_view_open", "operator": "equal", "operand": false }], 86 | "keys": ["ctrl+f11"] 87 | }, 88 | { 89 | "command": "gdb_prev_cmd", 90 | "context": [{"key": "gdb_input_view"}], 91 | "keys": ["up"] 92 | }, 93 | { 94 | "command": "gdb_next_cmd", 95 | "context": [{"key": "gdb_input_view"}], 96 | "keys": ["down"] 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /Default.sublime-mousemap: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | { 4 | "button": "button1", "count": 1, 5 | "press_command": "drag_select", 6 | "command": "gdb_click" 7 | }, 8 | 9 | { 10 | "button": "button1", "count": 2, 11 | "press_command": "drag_select", 12 | "press_args": {"by": "words"}, 13 | "command": "gdb_double_click" 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Fredrik Ehnbom 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgement in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": 7 | [ 8 | { 9 | "caption": "Package Settings", 10 | "mnemonic": "P", 11 | "id": "package-settings", 12 | "children": 13 | [ 14 | { 15 | "caption": "SublimeGDB", 16 | "children": 17 | [ 18 | { 19 | "command": "open_file", "args": 20 | { 21 | "file": "${packages}/SublimeGDB/SublimeGDB.sublime-settings" 22 | }, 23 | "caption": "Settings – Default" 24 | }, 25 | { 26 | "command": "open_file", "args": 27 | { 28 | "file": "${packages}/User/SublimeGDB.sublime-settings" 29 | }, 30 | "caption": "Settings – User" 31 | }, 32 | { "caption": "-" }, 33 | { 34 | "command": "open_file", "args": 35 | { 36 | "file": "${packages}/SublimeGDB/Default.sublime-keymap" 37 | }, 38 | "caption": "Key Bindings – Default" 39 | }, 40 | { 41 | "command": "open_file", "args": 42 | { 43 | "file": "${packages}/User/Default (OSX).sublime-keymap", 44 | "platform": "OSX" 45 | }, 46 | "caption": "Key Bindings – User" 47 | }, 48 | { 49 | "command": "open_file", "args": 50 | { 51 | "file": "${packages}/User/Default (Linux).sublime-keymap", 52 | "platform": "Linux" 53 | }, 54 | "caption": "Key Bindings – User" 55 | }, 56 | { 57 | "command": "open_file", 58 | "args": { 59 | "file": "${packages}/User/Default (Windows).sublime-keymap", 60 | "platform": "Windows" 61 | }, 62 | "caption": "Key Bindings – User" 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | ] 71 | -------------------------------------------------------------------------------- /Readme.creole: -------------------------------------------------------------------------------- 1 | === Description === 2 | GDB plugin for Sublime Text 2. Filing issues are not welcome (and thus disabled). Pull requests please. 3 | 4 | === Installation === 5 | # The easiest way to install SublimeGDB is via the excellent Package Control Plugin 6 | ## See http://wbond.net/sublime_packages/package_control/installation 7 | ### Once package control has been installed, bring up the command palette (cmd+shift+P or ctrl+shift+P) 8 | ### Type Install and select "Package Control: Install Package" 9 | ### Select SublimeGDB from the list. Package Control will keep it automatically updated for you 10 | ## If you don't want to use package control, you can manually install it 11 | ### Go to your packages directory and type: 12 | #### git clone https://github.com/quarnster/SublimeGDB SublimeGDB 13 | # Back in the editor, open up the command palette by pressing cmd+shift+P or ctrl+shift+P 14 | # Type SublimeGDB and open up the settings file you want to modify 15 | 16 | === Usage === 17 | 18 | See [[https://github.com/quarnster/SublimeGDB/blob/master/Default.sublime-keymap|the default key bindings]] and [[https://github.com/quarnster/SublimeGDB/blob/master/Default.sublime-mousemap|the default mouse map]]. 19 | 20 | In short: 21 | * Open up the default settings via the command palette and begin typing SublimeGDB and select the Default SublimeGDB preferences. 22 | * See what options are available, and open up the User SublimeGDB preferences to tweak any values 23 | * If you have multiple projects, you most likely want to put project specific setting in your project file, with a prefixed "sublimegdb_". See the comments at the top of the default SublimeGDB preferences for an example. 24 | * If you have multiple executables in the same project, you can add a "sublimegdb_executables" setting to your project settings, and add an entry for each executable's settings. 25 | * Once you're all configured, you can toggle breakpoints with F9 (OSX Users might want to change the key binding, or disable the "Exposé and Spaces" key bindings in the System Preferences) 26 | * Launch with F5 27 | * Step over with F10 28 | * Step into with F11 29 | * Step out with Shift+F11 30 | * Click on the appropriate line in the GDB Callstack view to go to that stack frame 31 | * Click a variable in the GDB Variables view to show its children (if available) 32 | * Double click a variable in the GDB Variables view to modify its value 33 | * You can also access some commands by right clicking in any view 34 | 35 | === Show your support === 36 | 37 | [[https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UPSEP2BHMLYEW|Donate]] 38 | 39 | === License === 40 | This plugin is using the zlib license 41 | 42 | {{{ 43 | Copyright (c) 2012 Fredrik Ehnbom 44 | 45 | This software is provided 'as-is', without any express or implied 46 | warranty. In no event will the authors be held liable for any damages 47 | arising from the use of this software. 48 | 49 | Permission is granted to anyone to use this software for any purpose, 50 | including commercial applications, and to alter it and redistribute it 51 | freely, subject to the following restrictions: 52 | 53 | 1. The origin of this software must not be misrepresented; you must not 54 | claim that you wrote the original software. If you use this software 55 | in a product, an acknowledgment in the product documentation would be 56 | appreciated but is not required. 57 | 58 | 2. Altered source versions must be plainly marked as such, and must not be 59 | misrepresented as being the original software. 60 | 61 | 3. This notice may not be removed or altered from any source 62 | distribution. 63 | }}} 64 | 65 | -------------------------------------------------------------------------------- /SublimeGDB.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // All options in here can also be specified in your project settings 3 | // with a prepended "sublimegdb_". You probably want to 4 | // have something like this in your project settings: 5 | // 6 | // "settings": 7 | // { 8 | // "sublimegdb_workingdir": "${folder:${project_path:your_executable_name}}", 9 | // // NOTE: You MUST provide --interpreter=mi for the plugin to work 10 | // "sublimegdb_commandline": "gdb --interpreter=mi ./your_executable_name" 11 | // 12 | // } 13 | // 14 | // generalized pattern for using always the current open file with an executable name 15 | // as the current file 16 | // "settings": 17 | // { 18 | // "sublimegdb_workingdir": "${folder:${file}}", 19 | // // put your arguments hear 20 | // "sublimegdb_arguments": "", 21 | // // NOTE: You MUST provide --interpreter=mi for the plugin to work 22 | // "sublimegdb_commandline": "gdb --interpreter=mi --args ./${file_base_name}" 23 | // 24 | // 25 | // } 26 | // 27 | // If you want to debug different executables as part of the same project, you can add something 28 | // like this to your project settings: 29 | // 30 | // "settings": 31 | // { 32 | // "sublimegdb_executables": 33 | // { 34 | // "first_executable_name": 35 | // { 36 | // "workingdir": "${folder:${project_path:first_executable_name}}", 37 | // "commandline": "gdb --interpreter=mi ./first_executable" 38 | // }, 39 | // "second_executable_name": 40 | // { 41 | // "workingdir": "${folder:${project_path:second_executable_name}}", 42 | // "commandline": "gdb --interpreter=mi ./second_executable" 43 | // } 44 | // } 45 | // } 46 | // 47 | // When you start debugging, you will be prompted to choose from one of your executables. Any 48 | // settings not specified for that project will be searched in your project settings (with a 49 | // sublimegdb_ prefix), then in your user settings, then in the default settings. 50 | // 51 | // (Note: if you have multiple executables, and you have a breakpoint set in a source file which 52 | // is not included in the current executable, you may have to set either debug_ext or 53 | // i_know_how_to_use_gdb_thank_you_very_much.) 54 | // 55 | // ${home}, ${project_path:}, ${folder:}, ${file} and ${file_base_name} 56 | // tokens can be used in 'workingdir', 'commandline', 'arguments' options. 57 | // 58 | // ${home} is replaced with the value of the HOME environment variable. 59 | // 60 | // ${project_path:} tries to find a file with the given name in all the registered project folders and 61 | // returns the first file found, or the original file name if none is found. 62 | // Example: ${project_path:main.cpp} tries to find a file named "main.cpp" relative 63 | // to the current project's folders. If none is found, it is replaced with "main.cpp". 64 | // 65 | // ${folder:} is replaced with the dirname of the given path. 66 | // Example: ${folder:/path/to/file} is replaced with "/path/to". 67 | // "workingdir": "/tmp", 68 | // 69 | // ${file} is replaced with absolute path to currently open file (if any) 70 | // Example: /home/user/main.cpp 71 | // 72 | // ${file_base_name} is replaced with name without extension of currently 73 | // open file (if any) 74 | // Example: replaced with "main" for file "/home/user/main.cpp" 75 | "workingdir": "notset", 76 | 77 | // NOTE: You MUST provide --interpreter=mi for the plugin to work 78 | // "commandline": "gdb --interpreter=mi ./executable", 79 | "commandline": "notset", 80 | 81 | // Environments for running gdb and gdb server 82 | // Example: "env": {"DISPLAY": ":100"} 83 | "env": "notset", 84 | 85 | // Arguments for the program. 86 | // Example: to run "./executable foo bar" 87 | // "arguments": "foo bar" 88 | // To provide user input (stdin) use 89 | // "arguments": "< input.dat" 90 | "arguments": "", 91 | 92 | // GDB Server 93 | // Specify a command and working dir for launching a GDB Server 94 | // This is useful for dealing with "remote" servers that are actually locally 95 | // connected JTAG boxes 96 | "server_workingdir": "notset", 97 | "server_commandline": "notset", 98 | 99 | // The command to use to run the program. 100 | // If you are attaching to a remote program, you 101 | // probably want to change this to -exec-continue 102 | "exec_cmd": "-exec-run", 103 | 104 | // Load the image to the remote target 105 | "load_cmd": "-target-download", 106 | 107 | // Immediately run the target upload connecting 108 | // When attaching to a remote program, you 109 | // may want to set this to false 110 | "run_after_init": true, 111 | 112 | // Attempt to update stack information while the program is running 113 | // If your remote target does not support non-stop, set this to false 114 | "update_while_running" : true, 115 | 116 | // Attach to a remote target? This is needed here because "-gdb-set target-async 1" must be 117 | // done prior to attaching 118 | "attach_cmd" : "notset", 119 | 120 | // For the larger binaries with lot of shared libraries 121 | // the loading within the gdb could take much longer. 122 | // Configure the thread wait timeout by setting gdb_timeout 123 | "gdb_timeout": 20, 124 | 125 | // Define debugging window layout (window split) 126 | // first define column/row separators, then refer to them to define cells 127 | "layout": 128 | { 129 | "cols": [0.0, 0.33, 0.66, 1.0], 130 | "rows": [0.0, 0.75, 1.0], 131 | "cells": 132 | [ // c1 r1 c2 r2 133 | [0, 0, 3, 1], // -> (0.00, 0.00), (1.00, 0.75) 134 | [0, 1, 1, 2], // -> (0.00, 0.75), (0.33, 1.00) 135 | [1, 1, 2, 2], // -> (0.33, 0.75), (0.66, 1.00) 136 | [2, 1, 3, 2] // -> (0.66, 0.75), (1.00, 1.00) 137 | ] 138 | }, 139 | 140 | // visual stuff 141 | "breakpoint_scope": "keyword.gdb", 142 | "breakpoint_icon": "circle", 143 | "position_scope": "entity.name.class", 144 | "position_icon": "bookmark", 145 | "changed_variable_scope": "entity.name.class", 146 | "changed_variable_icon": "", 147 | 148 | // The group used for opening files 149 | "file_group": 0, 150 | 151 | "session_group": 1, 152 | "session_open": true, 153 | 154 | "console_group": 1, 155 | "console_open": true, 156 | 157 | "variables_group": 1, 158 | "variables_open": true, 159 | 160 | "callstack_group": 2, 161 | "callstack_open": true, 162 | 163 | "registers_group": 2, 164 | "registers_open": false, 165 | 166 | "disassembly_group": 2, 167 | "disassembly_open": false, 168 | // Set to "intel" for intel disassembly flavor. All other 169 | // values default to using "att" flavor. 170 | "disassembly_flavor": "intel", 171 | 172 | "threads_group": 3, 173 | "threads_open": true, 174 | 175 | "breakpoints_group": 3, 176 | "breakpoints_open": true, 177 | 178 | // If set to true will push the layout before debugging 179 | // and pop it when debugging ends 180 | "push_pop_layout": true, 181 | 182 | // If set to true will close the gdb views when the 183 | // debugging session ends 184 | "close_views": true, 185 | 186 | // File to optionally write all the raw data read from and written to the gdb session and the inferior program. 187 | // Setting it to "stdout" will write the output to the python console 188 | "debug_file": "stdout", 189 | 190 | // Add "pending breakpoints" for symbols that are dynamically loaded from 191 | // external shared libraries 192 | "debug_ext" : false, 193 | 194 | // Whether to log the raw data read from and written to the gdb session and the inferior program. 195 | "debug": true, 196 | 197 | // Enables pretty printing. For example: 198 | // 199 | // std::string testStdString = "Foobar" 200 | // -std::vector > someVectorOfInt = {...} 201 | // int [0] = 1 202 | // int [1] = 2 203 | // int [2] = 3 204 | // int [3] = 4 205 | // 206 | // To enable this feature, it should be enabled is gdb too, see this: https://sourceware.org/gdb/wiki/STLSupport 207 | // You should checkout latest printers: 208 | // svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python 209 | // And add to ~/.gdbinit the following: 210 | // 211 | // python 212 | // import sys 213 | // sys.path.insert(0, '') 214 | // from libstdcxx.v6.printers import register_libstdcxx_printers 215 | // register_libstdcxx_printers (None) 216 | // end 217 | // 218 | "enable_pretty_printing": true, 219 | 220 | // Disables showing the error message dialog when something goes wrong 221 | "i_know_how_to_use_gdb_thank_you_very_much": false 222 | } 223 | -------------------------------------------------------------------------------- /gdb.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | gdbinit 8 | 9 | name 10 | GDB 11 | patterns 12 | 13 | 14 | captures 15 | 16 | 1 17 | 18 | name 19 | punctuation.definition.comment.gdb 20 | 21 | 22 | comment 23 | Comment line. 24 | match 25 | ^\s*(#).*$\n? 26 | name 27 | comment.line.number-sign.gdb 28 | 29 | 30 | begin 31 | ^\s*(define)\ +(.*)? 32 | beginCaptures 33 | 34 | 0 35 | 36 | name 37 | meta.function 38 | 39 | 1 40 | 41 | name 42 | keyword.other.gdb 43 | 44 | 2 45 | 46 | name 47 | entity.name.function.gdb 48 | 49 | 50 | comment 51 | GDB Function Define 52 | end 53 | ^(end)$ 54 | endCaptures 55 | 56 | 1 57 | 58 | name 59 | keyword.other.gdb 60 | 61 | 62 | patterns 63 | 64 | 65 | include 66 | $self 67 | 68 | 69 | 70 | 71 | begin 72 | ^\s*(document)\ +(?:.*)? 73 | beginCaptures 74 | 75 | 1 76 | 77 | name 78 | keyword.other.gdb 79 | 80 | 81 | comment 82 | GDB Document 83 | end 84 | ^(end)$ 85 | endCaptures 86 | 87 | 1 88 | 89 | name 90 | keyword.other.gdb 91 | 92 | 93 | patterns 94 | 95 | 96 | match 97 | . 98 | name 99 | comment.block.documentation.gdb 100 | 101 | 102 | 103 | 104 | begin 105 | \" 106 | beginCaptures 107 | 108 | 0 109 | 110 | name 111 | punctuation.definition.string.begin.gdb 112 | 113 | 114 | comment 115 | GDB String 116 | end 117 | \" 118 | endCaptures 119 | 120 | 0 121 | 122 | name 123 | punctuation.definition.string.end 124 | 125 | 126 | name 127 | string.quoted.double.gdb 128 | patterns 129 | 130 | 131 | include 132 | #stringEscapedChar 133 | 134 | 135 | 136 | 137 | begin 138 | \' 139 | beginCaptures 140 | 141 | 0 142 | 143 | name 144 | punctuation.definition.string.begin.gdb 145 | 146 | 147 | comment 148 | GDB Character 149 | end 150 | \' 151 | endCaptures 152 | 153 | 0 154 | 155 | name 156 | punctuation.definition.string.end 157 | 158 | 159 | name 160 | string.quoted.single.gdb 161 | patterns 162 | 163 | 164 | include 165 | #stringEscapedChar 166 | 167 | 168 | 169 | 170 | begin 171 | ^\s*(echo) 172 | beginCaptures 173 | 174 | 1 175 | 176 | name 177 | keyword.other.gdb 178 | 179 | 180 | comment 181 | Echo statement 182 | end 183 | (?<!\\)\n 184 | patterns 185 | 186 | 187 | include 188 | #stringEscapedChar 189 | 190 | 191 | match 192 | \\$ 193 | name 194 | constant.character.escape.gdb 195 | 196 | 197 | match 198 | . 199 | name 200 | string.other.gdb 201 | 202 | 203 | 204 | 205 | comment 206 | GDB Number 207 | match 208 | \b(?:[0-9_]+|0x[0-9a-fA-F_]+)\b 209 | name 210 | constant.numeric.gdb 211 | 212 | 213 | comment 214 | GDB Variables 215 | match 216 | \$[@_a-zA-Z][@_a-zA-Z0-9]* 217 | name 218 | variable.other.gdb 219 | 220 | 221 | comment 222 | GDB Info 223 | match 224 | \b(?:address|architecture|args|breakpoints|catch|common|copying|dcache|display|files|float|frame|functions|handle|line|locals|program|registers|scope|set|sharedlibrary|signals|source|sources|stack|symbol|target|terminal|threads|syn|keyword|tracepoints|types|udot)\b 225 | name 226 | storage.type.gdb 227 | 228 | 229 | comment 230 | GDB Statement 231 | match 232 | ^\s*(?:actions|apply|apropos|attach|awatch|backtrace|break|bt|call|catch|cd|clear|collect|commands|complete|condition|continue|delete|detach|directory|disable|disassemble|display|down|dump|else|enable|end|file|finish|frame|handle|hbreak|help|if|ignore|inspect|jump|kill|list|load|maintenance|make|next|n|nexti|ni|output|overlay|passcount|path|po|print|p|printf|ptype|pwd|quit|rbreak|remote|return|run|r|rwatch|search|section|set|sharedlibrary|shell|show|si|signal|source|step|s|stepi|stepping|stop|target|tbreak|tdump|tfind|thbreak|thread|tp|trace|tstart|tstatus|tstop|tty|undisplay|unset|until|up|watch|whatis|where|while|ws|x|add-shared-symbol-files|add-symbol-file|core-file|dont-repeat|down-silently|exec-file|forward-search|reverse-search|save-tracepoints|select-frame|symbol-file|up-silently|while-stepping)\b 233 | name 234 | keyword.other.gdb 235 | 236 | 237 | comment 238 | GDB Set 239 | match 240 | \b(?:annotate|architecture|args|check|complaints|confirm|editing|endian|environment|gnutarget|height|history|language|listsize|print|prompt|radix|remotebaud|remotebreak|remotecache|remotedebug|remotedevice|remotelogbase|remotelogfile|remotetimeout|remotewritesize|targetdebug|variable|verbose|watchdog|width|write|auto-solib-add|solib-absolute-prefix|solib-search-path|stop-on-solib-events|symbol-reloading|input-radix|demangle-style|output-radix)\b 241 | name 242 | support.constant.gdb 243 | 244 | 245 | comment 246 | GDB Info 247 | match 248 | ^\s*info 249 | name 250 | constant.language.gdb 251 | 252 | 253 | repository 254 | 255 | stringEscapedChar 256 | 257 | patterns 258 | 259 | 260 | match 261 | \\(?:\\|[abefnprtv'"?]|[0-3]\d{0,2}|[4-7]\d?|x[a-fA-F0-9]{0,2}|u[a-fA-F0-9]{0,4}|U[a-fA-F0-9]{0,8}) 262 | name 263 | constant.character.escape.gdb 264 | 265 | 266 | match 267 | \\. 268 | name 269 | invalid.illegal.gdb 270 | 271 | 272 | 273 | 274 | scopeName 275 | source.gdb 276 | uuid 277 | 8d97021b-68d8-4b4b-b1c3-5b505e4a08e3 278 | 279 | 280 | -------------------------------------------------------------------------------- /gdb_disasm.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | GDB Disassembly 7 | patterns 8 | 9 | 10 | match 11 | .+:([0-9]+)$ 12 | name 13 | string.filename 14 | 15 | 16 | match 17 | \b0x[0-9a-f]+ 18 | name 19 | constant.other.hex.disasm 20 | 21 | 22 | match 23 | \b[0-9]+ 24 | name 25 | constant.other.disasm 26 | 27 | 28 | match 29 | [\w]+ 30 | name 31 | keyword.variable 32 | 33 | 34 | match 35 | \b([a-z\.]+) 36 | name 37 | support.function.disasm 38 | 39 | 40 | match 41 | (;|#)[^\d].*$ 42 | name 43 | comment 44 | 45 | 46 | scopeName 47 | source.disasm 48 | uuid 49 | 932CA89E-7D79-4406-9F53-8BC58525560C 50 | 51 | 52 | -------------------------------------------------------------------------------- /gdb_registers.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | GDB Registers 7 | patterns 8 | 9 | 10 | match 11 | \b.+: 12 | name 13 | keyword.gdbregs 14 | 15 | 16 | match 17 | \b[-$]*[0-9a-fx]+ 18 | name 19 | constant.other.gdbregs 20 | 21 | 22 | scopeName 23 | source.gdbregs 24 | uuid 25 | 04501C9F-007A-423E-8409-4560143F190E 26 | 27 | 28 | -------------------------------------------------------------------------------- /gdb_session.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | GDB Session 7 | repository 8 | 9 | string_escaped_char 10 | 11 | patterns 12 | 13 | 14 | match 15 | \\(\\|[abefnprtv'"?]|[0-3]\d{0,2}|[4-7]\d?|x[a-fA-F0-9]{0,2}|u[a-fA-F0-9]{0,4}|U[a-fA-F0-9]{0,8}) 16 | name 17 | constant.character.escape.c 18 | 19 | 20 | match 21 | \\. 22 | name 23 | invalid.illegal.unknown-escape.c 24 | 25 | 26 | 27 | 28 | patterns 29 | 30 | 31 | match 32 | ^([0-9]+)(-\S+) 33 | captures 34 | 35 | 1 36 | 37 | name 38 | constant.other.gdb.command 39 | 40 | 2 41 | 42 | name 43 | entity.name.function 44 | 45 | 46 | 47 | 48 | 49 | match 50 | ^~.*$ 51 | name 52 | comment 53 | 54 | 55 | match 56 | ^([0-9]+)(\^[^,]+) 57 | captures 58 | 59 | 1 60 | 61 | name 62 | constant.other.gdb.command 63 | 64 | 2 65 | 66 | name 67 | keyword.gdb.returncode 68 | 69 | 70 | 71 | 72 | 73 | begin 74 | " 75 | beginCaptures 76 | 77 | 0 78 | 79 | name 80 | punctuation.definition.string.begin.gdb 81 | 82 | 83 | end 84 | " 85 | endCaptures 86 | 87 | 0 88 | 89 | name 90 | punctuation.definition.string.end.gdb 91 | 92 | 93 | name 94 | string.quoted.double.gdb 95 | patterns 96 | 97 | 98 | include 99 | #string_escaped_char 100 | 101 | 102 | 103 | 104 | match 105 | [^{,=]+(?==) 106 | name 107 | storage.type.gdb 108 | 109 | 110 | scopeName 111 | source.gdb.session 112 | uuid 113 | 36F30C2C-0443-4944-9FA4-3940126FD71A 114 | 115 | 116 | -------------------------------------------------------------------------------- /resultparser.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import os 3 | import re 4 | import sublime 5 | 6 | cygwin_drive_regex = re.compile(r"^/cygdrive/([a-zA-Z])/") 7 | decoder = codecs.getdecoder('unicode_escape') 8 | 9 | def cygwin_path_handle(path): 10 | """Cygwin Path Support""" 11 | if sublime.platform() == "windows": 12 | return os.path.normcase(re.sub(cygwin_drive_regex, lambda m: "%s:/" % m.groups()[0], path)) 13 | else: 14 | return path # do nothing if it is not under windows. 15 | 16 | 17 | def add(d, key, value): 18 | if key == "file" or key == "fullname" or key == "original-location" or key == "from": 19 | value = cygwin_path_handle(value) 20 | if len(key) == 0: 21 | if len(d) == 0: 22 | d = [] 23 | d.append(value) 24 | #print "%s" % r 25 | else: 26 | if key not in d: 27 | d[key] = value 28 | else: 29 | if not isinstance(d[key], list): 30 | tmp = d[key] 31 | d[key] = [] 32 | d[key].append(tmp) 33 | d[key].append(value) 34 | return d 35 | 36 | 37 | def _parse_result_line(line): 38 | start = 0 39 | inComment = False 40 | key = "" 41 | value = "" 42 | i = 0 43 | subparse = 0 44 | d = {} 45 | while i < len(line): 46 | c = line[i] 47 | if inComment: 48 | if c == "\"": 49 | inComment = False 50 | value = decoder(line[start:i])[0] 51 | d = add(d, key, value) 52 | key = "" 53 | start = i + 1 54 | elif c == "\\": 55 | if line[i + 1] == "\"": 56 | i += 1 57 | else: 58 | if c == "=": 59 | key = line[start:i] 60 | start = i + 1 61 | elif c == "\"": 62 | inComment = True 63 | start = i + 1 64 | elif c == "," or c == " " or c == "\n" or c == "\r": 65 | start = i + 1 66 | elif c == "{" or c == "[": 67 | subparse += 1 68 | start = i + 1 69 | (pos, r) = _parse_result_line(line[start:]) 70 | d = add(d, key, r) 71 | i = start + pos 72 | continue 73 | elif c == "}" or c == "]": 74 | if subparse > 0: 75 | subparse -= 1 76 | else: 77 | break 78 | 79 | i += 1 80 | return (i, d) 81 | 82 | 83 | def parse_result_line(line): 84 | return _parse_result_line(line)[1] 85 | -------------------------------------------------------------------------------- /sublimegdb.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2012 Fredrik Ehnbom 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | """ 23 | import sublime 24 | import sublime_plugin 25 | import subprocess 26 | import struct 27 | import tempfile 28 | import threading 29 | import time 30 | import traceback 31 | import os 32 | import sys 33 | import re 34 | import queue 35 | from datetime import datetime 36 | from functools import partial 37 | try: 38 | import Queue 39 | from resultparser import parse_result_line 40 | 41 | def sencode(s): 42 | return s.encode("utf-8") 43 | 44 | def sdecode(s): 45 | return s 46 | 47 | def bencode(s): 48 | return s 49 | def bdecode(s): 50 | return s 51 | except: 52 | def sencode(s): 53 | return s 54 | 55 | def sdecode(s): 56 | return s 57 | 58 | def bencode(s): 59 | return s.encode("utf-8") 60 | 61 | def bdecode(s): 62 | return s.decode("utf-8") 63 | 64 | import queue as Queue 65 | from SublimeGDB.resultparser import parse_result_line 66 | 67 | exec_settings = {} 68 | 69 | def get_setting(key, default=None, view=None): 70 | try: 71 | if view is None: 72 | view = sublime.active_window().active_view() 73 | s = view.settings() 74 | 75 | # Try executable specific settings first 76 | if exec_settings and key in exec_settings: 77 | return exec_settings[key] 78 | # Then try user settings 79 | if s.has("sublimegdb_%s" % key): 80 | return s.get("sublimegdb_%s" % key) 81 | except: 82 | pass 83 | 84 | # Default settings 85 | return sublime.load_settings("SublimeGDB.sublime-settings").get(key, default) 86 | 87 | 88 | def expand_path(value, window): 89 | if window is None: 90 | # Views can apparently be window less, in most instances getting 91 | # the active_window will be the right choice (for example when 92 | # previewing a file), but the one instance this is incorrect 93 | # is during Sublime Text 2 session restore. Apparently it's 94 | # possible for views to be windowless then too and since it's 95 | # possible that multiple windows are to be restored, the 96 | # "wrong" one for this view might be the active one and thus 97 | # ${project_path} will not be expanded correctly. 98 | # 99 | # This will have to remain a known documented issue unless 100 | # someone can think of something that should be done plugin 101 | # side to fix this. 102 | window = sublime.active_window() 103 | 104 | get_existing_files = \ 105 | lambda m: [ path \ 106 | for f in window.folders() \ 107 | for path in [os.path.join(f, m.group('file'))] \ 108 | if os.path.exists(path) \ 109 | ] 110 | view = window.active_view() 111 | file_name = view.file_name(); 112 | # replace variable with values 113 | if file_name: 114 | value = re.sub(r'\${file}', lambda m: file_name, value) 115 | value = re.sub(r'\${file_base_name}', lambda m: os.path.splitext(os.path.basename(file_name))[0], value) 116 | if os.getenv("HOME"): 117 | value = re.sub(r'\${home}', re.escape(os.getenv('HOME')), value) 118 | value = re.sub(r'\${env:(?P.*)}', lambda m: os.getenv(m.group('variable')), value) 119 | # search in projekt for path and get folder from path 120 | value = re.sub(r'\${project_path:(?P[^}]+)}', lambda m: len(get_existing_files(m)) > 0 and get_existing_files(m)[0] or m.group('file'), value) 121 | value = re.sub(r'\${folder:(?P.*)}', lambda m: os.path.dirname(m.group('file')), value) 122 | value = value.replace('\\', os.sep) 123 | value = value.replace('/', os.sep) 124 | 125 | return value 126 | 127 | 128 | DEBUG = None 129 | DEBUG_FILE = None 130 | __debug_file_handle = None 131 | 132 | gdb_lastline = "" 133 | gdb_lastresult = queue.Queue() 134 | gdb_last_console_line = "" 135 | gdb_cursor = "" 136 | gdb_cursor_position = 0 137 | gdb_last_cursor_view = None 138 | gdb_bkp_layout = {} 139 | gdb_bkp_window = None 140 | gdb_bkp_view = None 141 | 142 | gdb_python_command_running = False 143 | gdb_shutting_down = False 144 | gdb_process = None 145 | gdb_server_process = None 146 | gdb_threads = [] 147 | gdb_stack_frame = None 148 | gdb_stack_index = 0 149 | 150 | gdb_nonstop = False 151 | 152 | if os.name == 'nt': 153 | gdb_nonstop = False 154 | 155 | gdb_run_status = None 156 | result_regex = re.compile("(?<=\^)[^,\"]*") 157 | collapse_regex = re.compile("{.*}", re.DOTALL) 158 | 159 | 160 | def normalize(filename): 161 | if filename is None: 162 | return None 163 | return os.path.abspath(os.path.normcase(filename)) 164 | 165 | 166 | def log_debug(line): 167 | global __debug_file_handle 168 | global DEBUG 169 | if DEBUG: 170 | try: 171 | if __debug_file_handle is None: 172 | if DEBUG_FILE == "stdout": 173 | __debug_file_handle = sys.stdout 174 | else: 175 | __debug_file_handle = open(DEBUG_FILE, 'a') 176 | __debug_file_handle.write(line) 177 | except: 178 | sublime.error_message("Couldn't write to the debug file. Debug writes will be disabled for this session.\n\nDebug file name used:\n%s\n\nError message\n:%s" % (DEBUG_FILE, traceback.format_exc())) 179 | DEBUG = False 180 | 181 | 182 | class GDBView(object): 183 | def __init__(self, name, s=True, settingsprefix=None): 184 | self.queue = Queue.Queue() 185 | self.name = name 186 | self.closed = True 187 | self.doScroll = s 188 | self.view = None 189 | self.settingsprefix = settingsprefix 190 | self.timer = None 191 | self.lines = "" 192 | self.lock = threading.RLock() 193 | 194 | def is_open(self): 195 | return not self.closed 196 | 197 | def open_at_start(self): 198 | if self.settingsprefix is not None: 199 | return get_setting("%s_open" % self.settingsprefix, False) 200 | return False 201 | 202 | def open(self): 203 | if self.view is None or self.view.window() is None: 204 | if self.settingsprefix is not None: 205 | sublime.active_window().focus_group(get_setting("%s_group" % self.settingsprefix, 0)) 206 | self.create_view() 207 | 208 | def close(self): 209 | if self.view is not None: 210 | if self.settingsprefix is not None: 211 | sublime.active_window().focus_group(get_setting("%s_group" % self.settingsprefix, 0)) 212 | self.destroy_view() 213 | 214 | def should_update(self): 215 | return self.is_open() and is_running() and gdb_run_status == "stopped" 216 | 217 | def set_syntax(self, syntax): 218 | if self.is_open(): 219 | self.get_view().set_syntax_file(syntax) 220 | 221 | 222 | def timed_add(self): 223 | try: 224 | self.lock.acquire() 225 | lines = self.lines 226 | self.lines = "" 227 | self.timer = None 228 | self.queue.put((self.do_add_line, lines)) 229 | sublime.set_timeout(self.update, 0) 230 | finally: 231 | self.lock.release() 232 | 233 | 234 | def add_line(self, line, now=True): 235 | if self.is_open(): 236 | try: 237 | self.lock.acquire() 238 | self.lines += line 239 | if self.timer: 240 | self.timer.cancel() 241 | if self.lines.count("\n") > 10 or now: 242 | self.timed_add() 243 | else: 244 | self.timer = threading.Timer(0.1, self.timed_add) 245 | self.timer.start() 246 | finally: 247 | self.lock.release() 248 | 249 | def scroll(self, line): 250 | if self.is_open(): 251 | self.queue.put((self.do_scroll, line)) 252 | sublime.set_timeout(self.update, 0) 253 | 254 | def set_viewport_position(self, pos): 255 | if self.is_open(): 256 | self.queue.put((self.do_set_viewport_position, pos)) 257 | sublime.set_timeout(self.update, 0) 258 | 259 | def clear(self, now=False): 260 | if self.is_open(): 261 | if not now: 262 | self.queue.put((self.do_clear, None)) 263 | sublime.set_timeout(self.update, 0) 264 | else: 265 | self.do_clear(None) 266 | 267 | def create_view(self): 268 | self.view = sublime.active_window().new_file() 269 | self.view.set_name(self.name) 270 | self.view.set_scratch(True) 271 | self.view.set_read_only(True) 272 | self.view.settings().set("scroll_past_end", False) 273 | # Setting command_mode to false so that vintage 274 | # does not eat the "enter" keybinding 275 | self.view.settings().set('command_mode', False) 276 | self.closed = False 277 | 278 | def destroy_view(self): 279 | sublime.active_window().focus_view(self.view) 280 | sublime.active_window().run_command("close") 281 | self.view = None 282 | self.closed = True 283 | 284 | def is_closed(self): 285 | return self.closed 286 | 287 | def was_closed(self): 288 | self.closed = True 289 | 290 | def fold_all(self): 291 | if self.is_open(): 292 | self.queue.put((self.do_fold_all, None)) 293 | 294 | def get_view(self): 295 | return self.view 296 | 297 | def do_add_line(self, line): 298 | self.view.run_command("gdb_view_add_line", {"line": line, "doScroll": self.doScroll}) 299 | 300 | def do_fold_all(self, data): 301 | self.view.run_command("fold_all") 302 | 303 | def do_clear(self, data): 304 | self.view.run_command("gdb_view_clear") 305 | 306 | def do_scroll(self, data): 307 | self.view.run_command("goto_line", {"line": data + 1}) 308 | 309 | def do_move_to_eof(self): 310 | if self.view: 311 | self.view.run_command("move_to", { "to": "eof", "extend": False }) 312 | 313 | def do_set_viewport_position(self, data): 314 | # Shouldn't have to call viewport_extent, but it 315 | # seems to flush whatever value is stale so that 316 | # the following set_viewport_position works. 317 | # Keeping it around as a WAR until it's fixed 318 | # in Sublime Text 2. 319 | self.view.viewport_extent() 320 | self.view.set_viewport_position(data, False) 321 | 322 | def update(self): 323 | if not self.is_open(): 324 | return 325 | try: 326 | while not self.queue.empty(): 327 | cmd, data = self.queue.get() 328 | try: 329 | cmd(data) 330 | finally: 331 | self.queue.task_done() 332 | except: 333 | traceback.print_exc() 334 | 335 | def on_activated(self): 336 | # scroll to the end of the view on first activation 337 | if self.doScroll and self.view.visible_region().empty(): 338 | # need a timeout because apparently a view can't be scrolled until 339 | # it has been fully activated once 340 | sublime.set_timeout(self.do_move_to_eof, 20) 341 | 342 | def on_session_ended(self): 343 | if get_setting("%s_clear_on_end" % self.settingsprefix, True): 344 | self.clear() 345 | 346 | 347 | class GdbViewClear(sublime_plugin.TextCommand): 348 | def run(self, edit): 349 | self.view.set_read_only(False) 350 | self.view.erase(edit, sublime.Region(0, self.view.size())) 351 | self.view.set_read_only(True) 352 | 353 | class GdbViewAddLine(sublime_plugin.TextCommand): 354 | def run(self, edit, line, doScroll): 355 | # force a scroll to the end if the last line is currently visible 356 | force_scroll = False 357 | if doScroll and self.view.visible_region().contains(self.view.size()): 358 | force_scroll = True 359 | 360 | self.view.run_command("append", { "characters": line, "force": True }) 361 | 362 | if force_scroll: 363 | self.view.run_command("move_to", { "to": "eof", "extend": False }) 364 | 365 | class GDBVariable: 366 | def __init__(self, vp=None, parent=None): 367 | self.parent = parent 368 | self.valuepair = vp 369 | self.children = [] 370 | self.line = 0 371 | self.is_expanded = False 372 | if "value" not in vp: 373 | self.update_value() 374 | self.dirty = False 375 | self.deleted = False 376 | 377 | def delete(self): 378 | run_cmd("-var-delete %s" % self.get_name()) 379 | self.deleted = True 380 | 381 | def update_value(self): 382 | line = run_cmd("-var-evaluate-expression %s" % self["name"], True) 383 | if get_result(line) == "done": 384 | self['value'] = parse_result_line(line)["value"] 385 | 386 | def update(self, d): 387 | for key in d: 388 | if key.startswith("new_"): 389 | if key == "new_num_children": 390 | self["numchild"] = d[key] 391 | else: 392 | self[key[4:]] = d[key] 393 | else: 394 | self[key] = d[key] 395 | if (('dynamic' in self) and ('dynamic' not in d)): 396 | del self['dynamic'] 397 | 398 | def is_existing(self): 399 | # if there is a parent this variable should be existing 400 | if self.parent: 401 | return True 402 | 403 | # try to find the line where this variable was declared 404 | output = run_python_cmd("python print(gdb.lookup_symbol(\"%s\")[0].line)" % self["exp"], True) 405 | try: 406 | line = int(output) 407 | 408 | # if the cursor is after this position the variable should be 409 | # existing 410 | if gdb_cursor_position > line: 411 | return True 412 | except: 413 | pass 414 | 415 | return False 416 | 417 | def get_expression(self): 418 | expression = "" 419 | parent = self.parent 420 | while parent is not None: 421 | ispointer = "typecode" in parent and parent["typecode"] == "PTR" 422 | expression = "%s%s%s" % (parent["exp"], "->" if ispointer else ".", expression) 423 | parent = parent.parent 424 | expression += self["exp"] 425 | return expression 426 | 427 | def add_children(self, name): 428 | children = listify(parse_result_line(run_cmd("-var-list-children 1 \"%s\"" % name, True))["children"]["child"]) 429 | for child in children: 430 | child = GDBVariable(child, parent=self) 431 | if child.get_name().endswith(".private") or \ 432 | child.get_name().endswith(".protected") or \ 433 | child.get_name().endswith(".public"): 434 | if child.has_children(): 435 | self.add_children(child.get_name()) 436 | else: 437 | self.children.append(child) 438 | 439 | def is_editable(self): 440 | line = run_cmd("-var-show-attributes %s" % (self.get_name()), True) 441 | return "editable" in re.findall("(?<=attr=\")[a-z]+(?=\")", line) 442 | 443 | def edit_on_done(self, val): 444 | line = run_cmd("-var-assign %s \"%s\"" % (self.get_name(), val), True) 445 | if get_result(line) == "done": 446 | self.valuepair["value"] = parse_result_line(line)["value"] 447 | gdb_variables_view.update_variables(True) 448 | else: 449 | err = line[line.find("msg=") + 4:] 450 | sublime.status_message("Error: %s" % err) 451 | 452 | def find(self, name): 453 | if self.deleted: 454 | return None 455 | if name == self.get_name(): 456 | return self 457 | elif name.startswith(self.get_name()): 458 | for child in self.children: 459 | ret = child.find(name) 460 | if ret is not None: 461 | return ret 462 | return None 463 | 464 | def edit(self): 465 | sublime.active_window().show_input_panel("%s =" % self["exp"], self.valuepair["value"], self.edit_on_done, None, None) 466 | 467 | def get_name(self): 468 | return self.valuepair["name"] 469 | 470 | def expand(self): 471 | if not self.is_existing(): 472 | return 473 | 474 | self.is_expanded = True 475 | if ((not self.children) and self.has_children()): 476 | self.add_children(self.get_name()) 477 | 478 | def has_children(self): 479 | if (int(self["numchild"]) > 0 or 480 | (self.is_dynamic and int(self.valuepair.get("has_more", 0)) > 0)): 481 | return True 482 | 483 | # for dynamic child variables the has_more field is not available so we 484 | # have to actually list the children to find out if there are any 485 | if self.is_dynamic and self.is_existing(): 486 | children = parse_result_line(run_cmd("-var-list-children \"%s\"" % self.get_name(), True)) 487 | return int(children["numchild"]) > 0 488 | 489 | return False 490 | 491 | def collapse(self): 492 | self.is_expanded = False 493 | 494 | def __str__(self): 495 | # apply the user-defined filters to the type 496 | type = self.filter_type(self['type']) 497 | 498 | if not "dynamic_type" in self or len(self['dynamic_type']) == 0 or self['dynamic_type'] == self['type']: 499 | return "%s %s = %s" % (type, self['exp'], self['value']) 500 | else: 501 | return "%s %s = (%s) %s" % (type, self['exp'], self['dynamic_type'], self['value']) 502 | 503 | def __iter__(self): 504 | return self.valuepair.__iter__() 505 | 506 | def __getitem__(self, key): 507 | return self.valuepair[key] 508 | 509 | def __setitem__(self, key, value): 510 | self.valuepair[key] = value 511 | if key == "value": 512 | self.dirty = True 513 | 514 | @property 515 | def is_dynamic(self): 516 | return ('dynamic' in self) 517 | 518 | def update_from(self, var): 519 | if (var.is_expanded): 520 | self.expand() 521 | for child in self.children: 522 | otherChild = var.find_child_expression(child["exp"]) 523 | if (otherChild): 524 | child.update_from(otherChild) 525 | else: 526 | child.dirty = True 527 | self.dirty = (self["value"] != var["value"]) 528 | 529 | def find_child_expression(self, exp): 530 | for child in self.children: 531 | if (child["exp"] == exp): 532 | return child 533 | return None 534 | 535 | def clear_dirty(self): 536 | self.dirty = False 537 | for child in self.children: 538 | child.clear_dirty() 539 | 540 | def is_dirty(self): 541 | dirt = self.dirty 542 | if not dirt and not self.is_expanded: 543 | for child in self.children: 544 | if child.is_dirty(): 545 | dirt = True 546 | break 547 | return dirt 548 | 549 | def format(self, indent="", output="", line=0, dirty=[]): 550 | icon = " " 551 | if self.has_children(): 552 | if self.is_expanded: 553 | icon = "-" 554 | else: 555 | icon = "+" 556 | 557 | output += "%s%s%s\n" % (indent, icon, self) 558 | self.line = line 559 | line = line + 1 560 | indent += " " 561 | if self.is_expanded: 562 | for child in self.children: 563 | output, line = child.format(indent, output, line, dirty) 564 | if self.is_dirty(): 565 | dirty.append(self) 566 | return (output, line) 567 | 568 | @staticmethod 569 | def filter_type(type): 570 | # use the regex module instead of re if available 571 | sub = re.sub 572 | try: 573 | import regex 574 | sub = regex.sub 575 | except: 576 | pass 577 | 578 | # apply all user-defined filters 579 | filters = get_setting("type_filters", [], gdb_variables_view) 580 | for f in filters: 581 | type = sub(f["pattern"], f["replace"], type) 582 | 583 | return type 584 | 585 | def qtod(q): 586 | val = struct.pack("Q", q) 587 | return struct.unpack("d", val)[0] 588 | 589 | def itof(i): 590 | val = struct.pack("I", i) 591 | return struct.unpack("f", val)[0] 592 | 593 | class GDBRegister: 594 | def __init__(self, name, index, val): 595 | self.name = name 596 | self.index = index 597 | self.value = val 598 | self.line = 0 599 | self.lines = 0 600 | 601 | def format(self, line=0): 602 | val = self.value 603 | if "{" not in val and re.match(r"[\da-yA-Fx]+", val): 604 | valh = int(val, 16)&0xffffffffffffffffffffffffffffffff 605 | six4 = False 606 | if valh > 0xffffffff: 607 | six4 = True 608 | val = struct.pack("Q" if six4 else "I", valh) 609 | valf = struct.unpack("d" if six4 else "f", val)[0] 610 | valI = struct.unpack("Q" if six4 else "I", val)[0] 611 | vali = struct.unpack("q" if six4 else "i", val)[0] 612 | 613 | val = "0x%016x %16.8f %020d %020d" % (valh, valf, valI, vali) 614 | elif "{" in val: 615 | match = re.search(r"(.*v4_float\s*=\s*\{)([^}]+)(\}.*v4_int32\s*=\s*\{([^\}]+)\}.*)", val) 616 | if match: 617 | floats = re.findall(r"0x[^,\}]+", match.group(4)) 618 | if len(floats) == 4: 619 | floats = [str(itof(int(f, 16))) for f in floats] 620 | val = match.expand(r"\g<1>%s\g<3>" % ", ".join(floats)) 621 | match = re.search(r"(.*v2_double\s*=\s*\{)([^}]+)(\}.*v2_int64\s*=\s*\{([^\}]+)\}.*)", val) 622 | if match: 623 | doubles = re.findall(r"0x[^,\}]+", match.group(4)) 624 | if len(doubles) == 2: 625 | doubles = [str(qtod(int(f, 16))) for f in doubles] 626 | val = match.expand(r"\g<1>%s\g<3>" % ", ".join(doubles)) 627 | 628 | output = "%8s: %s\n" % (self.name, val) 629 | self.line = line 630 | line += output.count("\n") 631 | self.lines = line - self.line 632 | return (output, line) 633 | 634 | def set_value(self, val): 635 | self.value = val 636 | 637 | def set_gdb_value(self, val): 638 | if "." in val: 639 | if val.endswith("f"): 640 | val = struct.unpack("I", struct.pack("f", float(val[:-1])))[0] 641 | else: 642 | val = struct.unpack("Q", struct.pack("d", float(val)))[0] 643 | 644 | run_cmd("-data-evaluate-expression $%s=%s" % (self.name, val)) 645 | 646 | def edit_on_done(self, val): 647 | self.set_gdb_value(val) 648 | gdb_register_view.update_values() 649 | 650 | def edit(self): 651 | sublime.active_window().show_input_panel("$%s =" % self.name, self.value, self.edit_on_done, None, None) 652 | 653 | 654 | class GDBRegisterView(GDBView): 655 | def __init__(self): 656 | super(GDBRegisterView, self).__init__("GDB Registers", s=False, settingsprefix="registers") 657 | self.values = None 658 | 659 | def open(self): 660 | super(GDBRegisterView, self).open() 661 | self.set_syntax("Packages/SublimeGDB/gdb_registers.tmLanguage") 662 | self.get_view().settings().set("word_wrap", False) 663 | if self.is_open() and gdb_run_status == "stopped": 664 | self.update_values() 665 | 666 | def get_names(self): 667 | line = run_cmd("-data-list-register-names", True) 668 | return parse_result_line(line)["register-names"] 669 | 670 | def get_values(self): 671 | line = run_cmd("-data-list-register-values x", True) 672 | if get_result(line) != "done": 673 | return [] 674 | return parse_result_line(line)["register-values"] 675 | 676 | def update_values(self): 677 | if not self.should_update(): 678 | return 679 | dirtylist = [] 680 | if self.values is None: 681 | names = self.get_names() 682 | vals = self.get_values() 683 | self.values = [] 684 | 685 | for i in range(len(vals)): 686 | idx = int(vals[i]["number"]) 687 | self.values.append(GDBRegister(names[idx], idx, vals[i]["value"])) 688 | else: 689 | dirtylist = regs = parse_result_line(run_cmd("-data-list-changed-registers", True))["changed-registers"] 690 | regvals = parse_result_line(run_cmd("-data-list-register-values x %s" % " ".join(regs), True))["register-values"] 691 | for i in range(len(regs)): 692 | reg = int(regvals[i]["number"]) 693 | if reg < len(self.values): 694 | self.values[reg].set_value(regvals[i]["value"]) 695 | pos = self.get_view().viewport_position() 696 | self.clear() 697 | line = 0 698 | for item in self.values: 699 | output, line = item.format(line) 700 | self.add_line(output) 701 | self.set_viewport_position(pos) 702 | self.update() 703 | regions = [] 704 | v = self.get_view() 705 | for dirty in dirtylist: 706 | i = int(dirty) 707 | if i >= len(self.values): 708 | continue 709 | region = v.full_line(v.text_point(self.values[i].line, 0)) 710 | if self.values[i].lines > 1: 711 | region = region.cover(v.full_line(v.text_point(self.values[i].line + self.values[i].lines - 1, 0))) 712 | 713 | regions.append(region) 714 | v.add_regions("sublimegdb.dirtyregisters", regions, 715 | get_setting("changed_variable_scope", "entity.name.class"), 716 | get_setting("changed_variable_icon", ""), 717 | sublime.DRAW_OUTLINED) 718 | 719 | def get_register_at_line(self, line): 720 | if self.values is None: 721 | return None 722 | for i in range(len(self.values)): 723 | if self.values[i].line == line: 724 | return self.values[i] 725 | elif self.values[i].line > line: 726 | return self.values[i - 1] 727 | return None 728 | 729 | 730 | class GDBVariablesView(GDBView): 731 | def __init__(self): 732 | super(GDBVariablesView, self).__init__("GDB Variables", False, settingsprefix="variables") 733 | self.variables = [] 734 | 735 | def open(self): 736 | super(GDBVariablesView, self).open() 737 | self.set_syntax("Packages/C++/C++.tmLanguage") 738 | if self.is_open() and gdb_run_status == "stopped": 739 | self.update_variables(False) 740 | 741 | def update_view(self): 742 | self.clear() 743 | output = "" 744 | line = 0 745 | dirtylist = [] 746 | for local in self.variables: 747 | output, line = local.format(line=line, dirty=dirtylist) 748 | self.add_line(output) 749 | self.update() 750 | regions = [] 751 | v = self.get_view() 752 | for dirty in dirtylist: 753 | regions.append(v.full_line(v.text_point(dirty.line, 0))) 754 | v.add_regions("sublimegdb.dirtyvariables", regions, 755 | get_setting("changed_variable_scope", "entity.name.class"), 756 | get_setting("changed_variable_icon", ""), 757 | sublime.DRAW_OUTLINED) 758 | 759 | def extract_varnames(self, res): 760 | if "name" in res: 761 | return listify(res["name"]) 762 | elif len(res) > 0 and isinstance(res, list): 763 | if "name" in res[0]: 764 | return [x["name"] for x in res] 765 | return [] 766 | 767 | def add_variable(self, exp): 768 | v = self.create_variable(exp) 769 | if v: 770 | self.variables.append(v) 771 | 772 | def create_variable(self, exp, show_error = True): 773 | line = run_cmd("-var-create - * %s" % exp, True) 774 | if get_result(line, False) == "error" and "&" in exp: 775 | line = run_cmd("-var-create - * %s" % exp.replace("&", ""), True) 776 | if get_result(line, show_error) == "error": 777 | return None 778 | var = parse_result_line(line) 779 | var['exp'] = exp 780 | return GDBVariable(var) 781 | 782 | def update_variables(self, sameFrame): 783 | if not self.should_update(): 784 | return 785 | if sameFrame: 786 | variables = [] 787 | for var in self.variables: # completely replace dynamic variables because we don't always get proper update notifications 788 | if (var.is_dynamic): 789 | var.delete() 790 | newVar = self.create_variable(var['exp'], False) 791 | if (newVar): # may have gone out of scope without notification 792 | newVar.update_from(var) 793 | variables.append(newVar) 794 | else: 795 | var.clear_dirty() 796 | variables.append(var) 797 | self.variables = variables 798 | ret = parse_result_line(run_cmd("-var-update --all-values *", True))["changelist"] 799 | if "varobj" in ret: 800 | ret = listify(ret["varobj"]) 801 | dellist = [] 802 | for value in ret: 803 | name = value["name"] 804 | for var in self.variables: 805 | real = var.find(name) 806 | if (real is not None): 807 | if "in_scope" in value and value["in_scope"] == "false": 808 | real.delete() 809 | dellist.append(real) 810 | continue 811 | if (not real.is_dynamic): 812 | real.update(value) 813 | if not "value" in value and not "new_value" in value: 814 | real.update_value() 815 | break 816 | for item in dellist: 817 | self.variables.remove(item) 818 | if len(self.variables) == 0: 819 | # Is it really the same frame? Seems everything was removed, so might as well pull all data again 820 | sameFrame = False 821 | else: 822 | loc = self.extract_varnames(parse_result_line(run_cmd("-stack-list-locals 0", True))["locals"]) 823 | tracked = [] 824 | for var in loc: 825 | create = True 826 | for var2 in self.variables: 827 | if var2['exp'] == var and var2 not in tracked: 828 | tracked.append(var2) 829 | create = False 830 | break 831 | if create: 832 | self.add_variable(var) 833 | 834 | if not sameFrame: 835 | for var in self.variables: 836 | var.delete() 837 | args = self.extract_varnames(parse_result_line(run_cmd("-stack-list-arguments 0 %d %d" % (gdb_stack_index, gdb_stack_index), True))["stack-args"]["frame"]["args"]) 838 | self.variables = [] 839 | for arg in args: 840 | self.add_variable(arg) 841 | loc = self.extract_varnames(parse_result_line(run_cmd("-stack-list-locals 0", True))["locals"]) 842 | for var in loc: 843 | self.add_variable(var) 844 | self.update_view() 845 | 846 | def get_variable_at_line(self, line, var_list=None): 847 | if var_list is None: 848 | var_list = self.variables 849 | if len(var_list) == 0: 850 | return None 851 | 852 | for i in range(len(var_list)): 853 | if var_list[i].line == line: 854 | return var_list[i] 855 | elif var_list[i].line > line: 856 | return self.get_variable_at_line(line, var_list[i - 1].children) 857 | return self.get_variable_at_line(line, var_list[len(var_list) - 1].children) 858 | 859 | def expand_collapse_variable(self, view, expand=True, toggle=False): 860 | row, col = view.rowcol(view.sel()[0].a) 861 | if self.is_open() and view.id() == self.get_view().id(): 862 | var = self.get_variable_at_line(row) 863 | if var and var.has_children(): 864 | if toggle: 865 | if var.is_expanded: 866 | var.collapse() 867 | else: 868 | var.expand() 869 | elif expand: 870 | var.expand() 871 | else: 872 | var.collapse() 873 | pos = view.viewport_position() 874 | self.update_view() 875 | self.set_viewport_position(pos) 876 | self.update() 877 | 878 | 879 | class GDBCallstackFrame: 880 | def __init__(self, func, args): 881 | self.func = func 882 | self.args = args 883 | self.lines = 0 884 | 885 | def format(self): 886 | output = "%s(" % self.func 887 | for arg in self.args: 888 | if "name" in arg: 889 | output += arg["name"] 890 | if "value" in arg: 891 | val = arg["value"] 892 | val = collapse_regex.sub("{...}", val) 893 | output += " = %s" % val 894 | output += "," 895 | output += ");\n" 896 | self.lines = output.count("\n") 897 | return output 898 | 899 | 900 | class GDBCallstackView(GDBView): 901 | def __init__(self): 902 | super(GDBCallstackView, self).__init__("GDB Callstack", settingsprefix="callstack") 903 | self.frames = [] 904 | 905 | def open(self): 906 | super(GDBCallstackView, self).open() 907 | self.set_syntax("Packages/C++/C++.tmLanguage") 908 | if self.is_open() and gdb_run_status == "stopped": 909 | self.update_callstack() 910 | 911 | def update_callstack(self): 912 | if not self.should_update(): 913 | return 914 | global gdb_cursor_position 915 | line = run_cmd("-stack-list-frames", True) 916 | if get_result(line) == "error": 917 | gdb_cursor_position = 0 918 | update_view_markers() 919 | return 920 | frames = listify(parse_result_line(line)["stack"]["frame"]) 921 | args = listify(parse_result_line(run_cmd("-stack-list-arguments 1", True))["stack-args"]["frame"]) 922 | pos = self.get_view().viewport_position() 923 | self.clear() 924 | 925 | self.frames = [] 926 | for i in range(len(frames)): 927 | arg = {} 928 | if len(args) > i: 929 | arg = args[i]["args"] 930 | f = GDBCallstackFrame(frames[i]["func"], arg) 931 | self.frames.append(f) 932 | self.add_line(f.format()) 933 | self.set_viewport_position(pos) 934 | self.update() 935 | 936 | def update_marker(self, pos_scope, pos_icon): 937 | if self.is_open(): 938 | view = self.get_view() 939 | if gdb_stack_index != -1: 940 | line = 0 941 | for i in range(gdb_stack_index): 942 | line += self.frames[i].lines 943 | 944 | view.add_regions("sublimegdb.stackframe", 945 | [view.line(view.text_point(line, 0))], 946 | pos_scope, pos_icon, sublime.HIDDEN) 947 | else: 948 | view.erase_regions("sublimegdb.stackframe") 949 | 950 | def select(self, row): 951 | line = 0 952 | for i in range(len(self.frames)): 953 | fl = self.frames[i].lines 954 | if row <= line + fl - 1: 955 | run_cmd("-stack-select-frame %d" % i) 956 | update_cursor() 957 | break 958 | line += fl 959 | 960 | 961 | class GDBThread: 962 | def __init__(self, id, state="UNKNOWN", func="???()", details=None): 963 | self.id = id 964 | self.state = state 965 | self.func = func 966 | self.details = details 967 | 968 | def format(self): 969 | if self.details: 970 | return "%03d - %10s - %s - %s\n" % (self.id, self.state, self.details, self.func) 971 | else: 972 | return "%03d - %10s - %s\n" % (self.id, self.state, self.func) 973 | 974 | 975 | class GDBThreadsView(GDBView): 976 | def __init__(self): 977 | super(GDBThreadsView, self).__init__("GDB Threads", s=False, settingsprefix="threads") 978 | self.threads = [] 979 | self.current_thread = 0 980 | 981 | def open(self): 982 | super(GDBThreadsView, self).open() 983 | self.set_syntax("Packages/C++/C++.tmLanguage") 984 | if self.is_open() and gdb_run_status == "stopped": 985 | self.update_threads() 986 | 987 | def update_threads(self): 988 | if not self.should_update(): 989 | return 990 | res = run_cmd("-thread-info", True) 991 | ids = parse_result_line(run_cmd("-thread-list-ids", True)) 992 | if get_result(res) == "error": 993 | if "thread-ids" in ids and "thread-id" in ids["thread-ids"]: 994 | self.threads = [GDBThread(int(id)) for id in ids["thread-ids"]["thread-id"]] 995 | if "threads" in ids and "thread" in ids["threads"]: 996 | for thread in ids["threads"]["thread"]: 997 | if "thread-id" in thread and "state" in thread: 998 | tid = int(thread["thread-id"]) 999 | for t2 in self.threads: 1000 | if t2.id == tid: 1001 | t2.state = thread["state"] 1002 | break 1003 | else: 1004 | l = parse_result_line(run_cmd("-thread-info", True)) 1005 | else: 1006 | self.threads = [] 1007 | else: 1008 | l = parse_result_line(res) 1009 | self.threads = [] 1010 | for thread in l["threads"]: 1011 | func = "???" 1012 | if "frame" in thread and "func" in thread["frame"]: 1013 | func = thread["frame"]["func"] 1014 | args = "" 1015 | if "args" in thread["frame"]: 1016 | for arg in thread["frame"]["args"]: 1017 | if len(args) > 0: 1018 | args += ", " 1019 | if "name" in arg: 1020 | args += arg["name"] 1021 | if "value" in arg: 1022 | args += " = " + arg["value"] 1023 | func = "%s(%s);" % (func, args) 1024 | log_debug("thread %s" % thread) 1025 | self.threads.append(GDBThread(int(thread["id"]), thread["state"], func, thread.get("details"))) 1026 | 1027 | if "current-thread-id" in ids: 1028 | self.current_thread = int(ids["current-thread-id"]) 1029 | pos = self.get_view().viewport_position() 1030 | self.clear() 1031 | self.threads.sort(key=lambda t: t.id) 1032 | for thread in self.threads: 1033 | self.add_line(thread.format()) 1034 | self.set_viewport_position(pos) 1035 | self.update() 1036 | 1037 | def update_marker(self, pos_scope, pos_icon): 1038 | if self.is_open(): 1039 | view = self.get_view() 1040 | line = -1 1041 | for i in range(len(self.threads)): 1042 | if self.threads[i].id == self.current_thread: 1043 | line = i 1044 | break 1045 | 1046 | if line != -1: 1047 | view.add_regions("sublimegdb.currentthread", 1048 | [view.line(view.text_point(line, 0))], 1049 | pos_scope, pos_icon, sublime.HIDDEN) 1050 | else: 1051 | view.erase_regions("sublimegdb.currentthread") 1052 | 1053 | def select_thread(self, thread): 1054 | run_cmd("-thread-select %d" % thread) 1055 | self.current_thread = thread 1056 | 1057 | def select(self, row): 1058 | if row >= len(self.threads): 1059 | return 1060 | self.select_thread(self.threads[row].id) 1061 | 1062 | 1063 | class GDBDisassemblyView(GDBView): 1064 | def __init__(self): 1065 | super(GDBDisassemblyView, self).__init__("GDB Disassembly", s=False, settingsprefix="disassembly") 1066 | self.start = -1 1067 | self.end = -1 1068 | 1069 | def open(self): 1070 | super(GDBDisassemblyView, self).open() 1071 | self.set_syntax("Packages/SublimeGDB/gdb_disasm.tmLanguage") 1072 | self.get_view().settings().set("word_wrap", False) 1073 | if self.is_open() and gdb_run_status == "stopped": 1074 | self.update_disassembly() 1075 | 1076 | def clear(self): 1077 | super(GDBDisassemblyView, self).clear() 1078 | self.start = -1 1079 | self.end = -1 1080 | 1081 | def add_insns(self, src_asm): 1082 | for asm in src_asm: 1083 | line = "%s: %s" % (asm["address"], asm["inst"]) 1084 | if "func-name" in asm: 1085 | self.add_line("%-80s # %s+%s\n" % (line, asm["func-name"], asm["offset"])) 1086 | else: 1087 | self.add_line("%s\n" % line) 1088 | addr = int(asm["address"], 16) 1089 | if self.start == -1 or addr < self.start: 1090 | self.start = addr 1091 | self.end = addr 1092 | 1093 | def update_disassembly(self): 1094 | if not self.should_update(): 1095 | return 1096 | pc = parse_result_line(run_cmd("-data-evaluate-expression $pc", True))["value"] 1097 | if " " in pc: 1098 | pc = pc[:pc.find(" ")] 1099 | pc = int(pc, 16) 1100 | if not (pc >= self.start and pc <= self.end): 1101 | l = run_cmd("-data-disassemble -s \"$pc-32\" -e \"$pc+200\" -- 1", True) 1102 | asms = parse_result_line(l) 1103 | self.clear() 1104 | if get_result(l) != "error": 1105 | asms = asms["asm_insns"] 1106 | if "src_and_asm_line" in asms: 1107 | l = listify(asms["src_and_asm_line"]) 1108 | for src_asm in l: 1109 | line = src_asm["line"] 1110 | file = src_asm["file"] 1111 | self.add_line("%s:%s\n" % (file, line)) 1112 | self.add_insns(src_asm["line_asm_insn"]) 1113 | else: 1114 | self.add_insns(asms) 1115 | self.update() 1116 | view = self.get_view() 1117 | reg = view.find("^0x[0]*%x:" % pc, 0) 1118 | if reg is None: 1119 | view.erase_regions("sublimegdb.programcounter") 1120 | else: 1121 | pos_scope = get_setting("position_scope", "entity.name.class") 1122 | pos_icon = get_setting("position_icon", "bookmark") 1123 | view.add_regions("sublimegdb.programcounter", 1124 | [reg], 1125 | pos_scope, pos_icon, sublime.HIDDEN) 1126 | 1127 | 1128 | class GDBBreakpoint(object): 1129 | def __init__(self, filename="", line=0, addr=""): 1130 | self.original_filename = normalize(filename) 1131 | self.original_line = line 1132 | self.addr = addr 1133 | self.modified_line = None 1134 | self.clear() 1135 | self.add() 1136 | 1137 | @property 1138 | def line(self): 1139 | if self.modified_line: 1140 | return self.modified_line 1141 | if self.number != -1: 1142 | return self.resolved_line 1143 | return self.original_line 1144 | 1145 | @property 1146 | def filename(self): 1147 | if self.number != -1: 1148 | return normalize(self.resolved_filename) 1149 | return normalize(self.original_filename) 1150 | 1151 | def clear(self): 1152 | self.resolved_filename = "" 1153 | self.resolved_line = 0 1154 | self.number = -1 1155 | 1156 | if self.modified_line and not is_running(): 1157 | # the next GDB runs we will use the modified line 1158 | self.original_line = self.modified_line 1159 | self.modified_line = None 1160 | 1161 | def breakpoint_added(self, res): 1162 | if "bkpt" not in res: 1163 | return 1164 | bp = res["bkpt"] 1165 | if isinstance(bp, list): # choose the last of multiple entries 1166 | bp = bp[-1] 1167 | if "fullname" in bp: 1168 | self.resolved_filename = bp["fullname"] 1169 | elif "file" in bp: 1170 | self.resolved_filename = bp["file"] 1171 | elif "original-location" in bp and self.addr == 0: 1172 | self.resolved_filename = bp["original-location"].split(":", 1)[0] 1173 | self.resolved_line = int(bp["original-location"].split(":", 1)[1]) 1174 | 1175 | if self.resolved_line == 0 and "line" in bp: 1176 | self.resolved_line = int(bp["line"]) 1177 | 1178 | if not "/" in self.resolved_filename and not "\\" in self.resolved_filename: 1179 | self.resolved_filename = self.original_filename 1180 | self.number = int(bp["number"].split(".")[0]) 1181 | 1182 | def insert(self): 1183 | # TODO: does removing the unicode-escape break things? what's the proper way to handle this in python3? 1184 | # cmd = "-break-insert \"\\\"%s\\\":%d\"" % (self.original_filename.encode("unicode-escape"), self.original_line) 1185 | break_cmd = "-break-insert" 1186 | if get_setting("debug_ext") == True: 1187 | break_cmd += " -f" 1188 | if self.addr != "": 1189 | cmd = "%s *%s" % (break_cmd, self.addr) 1190 | else: 1191 | cmd = "%s \"\\\"%s\\\":%d\"" % (break_cmd, self.original_filename.replace("\\", "/"), self.original_line) 1192 | out = run_cmd(cmd, True) 1193 | if get_result(out) == "error": 1194 | return 1195 | res = parse_result_line(out) 1196 | if "bkpt" not in res and "matches" in res: 1197 | for match in res["matches"]["b"]: 1198 | cmd = "%s *%s" % (break_cmd, match["addr"]) 1199 | out = run_cmd(cmd, True) 1200 | if get_result(out) == "error": 1201 | return 1202 | res = parse_result_line(out) 1203 | self.breakpoint_added(res) 1204 | else: 1205 | self.breakpoint_added(res) 1206 | 1207 | def add(self): 1208 | if is_running(): 1209 | res = wait_until_stopped() 1210 | self.insert() 1211 | if res: 1212 | resume() 1213 | 1214 | def remove(self): 1215 | if is_running(): 1216 | res = wait_until_stopped() 1217 | run_cmd("-break-delete %s" % self.number) 1218 | if res: 1219 | resume() 1220 | 1221 | def format(self): 1222 | return "%d - %s:%d\n" % (self.number, self.filename, self.line) 1223 | 1224 | 1225 | class GDBWatch(GDBBreakpoint): 1226 | def __init__(self, exp): 1227 | self.exp = exp 1228 | super(GDBWatch, self).__init__(None, -1) 1229 | 1230 | def insert(self): 1231 | out = run_cmd("-break-watch %s" % self.exp, True) 1232 | res = parse_result_line(out) 1233 | if get_result(out) == "error": 1234 | return 1235 | 1236 | self.number = int(res["wpt"]["number"]) 1237 | 1238 | def format(self): 1239 | return "%d - watch: %s\n" % (self.number, self.exp) 1240 | 1241 | 1242 | class GDBBreakpointView(GDBView): 1243 | def __init__(self): 1244 | super(GDBBreakpointView, self).__init__("GDB Breakpoints", s=False, settingsprefix="breakpoints") 1245 | self.breakpoints = [] 1246 | 1247 | def open(self): 1248 | super(GDBBreakpointView, self).open() 1249 | # self.set_syntax("Packages/SublimeGDB/gdb_disasm.tmLanguage") 1250 | self.get_view().settings().set("word_wrap", False) 1251 | if self.is_open(): 1252 | self.update_view() 1253 | 1254 | def on_session_ended(self): 1255 | # Intentionally not calling super 1256 | for bkpt in self.breakpoints: 1257 | bkpt.clear() 1258 | 1259 | def on_view_modified(self, view): 1260 | if not self.breakpoints: 1261 | return 1262 | 1263 | fn = view.file_name() 1264 | if fn is None: 1265 | return 1266 | fn = normalize(fn) 1267 | 1268 | # use the modified regions to update the breakpoint locations 1269 | cur_regions = view.get_regions("sublimegdb.breakpoints") 1270 | 1271 | # list of breakpoints with a line for this view 1272 | bkpts = [] 1273 | for bkpt in self.breakpoints: 1274 | if bkpt.filename == fn and bkpt.addr == "": 1275 | bkpts.append(bkpt) 1276 | 1277 | # sorting the breakpoints by their last region brings them into the same 1278 | # order as the current regions 1279 | cur_regions.sort() 1280 | bkpts.sort(key=lambda bkpt: bkpt.last_region) 1281 | 1282 | need_update = False 1283 | for region, bkpt in zip(cur_regions, bkpts): 1284 | # if the current region's line and the breakpoint's differ we need 1285 | # to update it 1286 | new_line = view.rowcol(region.begin())[0] + 1 1287 | if new_line != bkpt.original_line: 1288 | if not is_running(): 1289 | bkpt.original_line = new_line 1290 | else: 1291 | # this will only update the visible location of the 1292 | # breakpoint but GDB will still stop at the original 1293 | # location 1294 | bkpt.modified_line = new_line 1295 | 1296 | need_update = True 1297 | 1298 | if need_update: 1299 | self.update_marker(view) 1300 | self.update_view() 1301 | 1302 | def update_marker(self, view): 1303 | bps = [] 1304 | fn = view.file_name() 1305 | if fn is None: 1306 | return 1307 | fn = normalize(fn) 1308 | for bkpt in self.breakpoints: 1309 | # also add one for the current cursor position to allow updating the 1310 | # breakpoints when the view is modified 1311 | if bkpt.filename == fn: 1312 | region = view.full_line(view.text_point(bkpt.line - 1, 0)) 1313 | 1314 | # save this region so that we can determine if it moved 1315 | bkpt.last_region = region 1316 | 1317 | bps.append(region) 1318 | 1319 | view.add_regions("sublimegdb.breakpoints", bps, 1320 | get_setting("breakpoint_scope", "keyword.gdb"), 1321 | get_setting("breakpoint_icon", "circle"), 1322 | sublime.HIDDEN) 1323 | 1324 | def find_breakpoint(self, filename, line): 1325 | filename = normalize(filename) 1326 | for bkpt in self.breakpoints: 1327 | if bkpt.filename == filename and bkpt.line == line: 1328 | return bkpt 1329 | return None 1330 | 1331 | def find_breakpoint_addr(self, addr): 1332 | for bkpt in self.breakpoints: 1333 | if bkpt.addr == addr: 1334 | return bkpt 1335 | return None 1336 | 1337 | def toggle_watch(self, exp): 1338 | add = True 1339 | for bkpt in self.breakpoints: 1340 | if isinstance(bkpt, GDBWatch) and bkpt.exp == exp: 1341 | add = False 1342 | bkpt.remove() 1343 | self.breakpoints.remove(bkpt) 1344 | break 1345 | 1346 | if add: 1347 | self.breakpoints.append(GDBWatch(exp)) 1348 | self.update_view() 1349 | 1350 | def toggle_breakpoint_addr(self, addr): 1351 | bkpt = self.find_breakpoint_addr(addr) 1352 | if bkpt: 1353 | bkpt.remove() 1354 | self.breakpoints.remove(bkpt) 1355 | else: 1356 | self.breakpoints.append(GDBBreakpoint(addr=addr)) 1357 | self.update_view() 1358 | 1359 | def toggle_breakpoint(self, filename, line): 1360 | bkpt = self.find_breakpoint(filename, line) 1361 | if bkpt: 1362 | bkpt.remove() 1363 | self.breakpoints.remove(bkpt) 1364 | else: 1365 | self.breakpoints.append(GDBBreakpoint(filename, line)) 1366 | self.update_view() 1367 | 1368 | def sync_breakpoints(self): 1369 | global breakpoints 1370 | for bkpt in self.breakpoints: 1371 | bkpt.add() 1372 | update_view_markers() 1373 | self.update_view() 1374 | 1375 | def update_view(self): 1376 | if not self.is_open(): 1377 | return 1378 | pos = self.get_view().viewport_position() 1379 | self.clear() 1380 | self.breakpoints.sort(key=lambda b: (b.number, b.filename or "", b.line)) 1381 | for bkpt in self.breakpoints: 1382 | self.add_line(bkpt.format()) 1383 | self.set_viewport_position(pos) 1384 | self.update() 1385 | 1386 | 1387 | class GDBSessionView(GDBView): 1388 | def __init__(self): 1389 | super(GDBSessionView, self).__init__("GDB Session", s=True, settingsprefix="session") 1390 | 1391 | def open(self): 1392 | super(GDBSessionView, self).open() 1393 | self.set_syntax("Packages/SublimeGDB/gdb_session.tmLanguage") 1394 | 1395 | 1396 | gdb_session_view = GDBSessionView() 1397 | gdb_console_view = GDBView("GDB Console", s=True, settingsprefix="console") 1398 | gdb_variables_view = GDBVariablesView() 1399 | gdb_callstack_view = GDBCallstackView() 1400 | gdb_register_view = GDBRegisterView() 1401 | gdb_disassembly_view = GDBDisassemblyView() 1402 | gdb_threads_view = GDBThreadsView() 1403 | gdb_breakpoint_view = GDBBreakpointView() 1404 | gdb_views = [gdb_session_view, gdb_console_view, gdb_variables_view, gdb_callstack_view, gdb_register_view, gdb_disassembly_view, gdb_threads_view, gdb_breakpoint_view] 1405 | 1406 | def update_view_markers(view=None): 1407 | if view is None: 1408 | view = sublime.active_window().active_view() 1409 | 1410 | fn = view.file_name() 1411 | if fn is not None: 1412 | fn = normalize(fn) 1413 | pos_scope = get_setting("position_scope", "entity.name.class") 1414 | pos_icon = get_setting("position_icon", "bookmark") 1415 | 1416 | cursor = [] 1417 | if fn == gdb_cursor and gdb_cursor_position != 0: 1418 | cursor.append(view.full_line(view.text_point(gdb_cursor_position - 1, 0))) 1419 | global gdb_last_cursor_view 1420 | if gdb_last_cursor_view is not None: 1421 | gdb_last_cursor_view.erase_regions("sublimegdb.position") 1422 | gdb_last_cursor_view = view 1423 | view.add_regions("sublimegdb.position", cursor, pos_scope, pos_icon, sublime.HIDDEN) 1424 | 1425 | gdb_callstack_view.update_marker(pos_scope, pos_icon) 1426 | gdb_threads_view.update_marker(pos_scope, pos_icon) 1427 | gdb_breakpoint_view.update_marker(view) 1428 | 1429 | count = 0 1430 | 1431 | 1432 | def run_cmd(cmd, block=False, mimode=True, timeout=None): 1433 | global count 1434 | if not is_running(): 1435 | return "0^error,msg=\"no session running\"" 1436 | 1437 | timeout = timeout or get_setting("gdb_command_timeout", 10) 1438 | 1439 | ### handle a list of commands by recursively calling run_cmd 1440 | if isinstance(cmd, list): 1441 | for c in cmd: 1442 | run_cmd(c, block, mimode, timeout) 1443 | return count 1444 | 1445 | if mimode: 1446 | count = count + 1 1447 | cmd = "%d%s\n" % (count, cmd) 1448 | else: 1449 | cmd = "%s\n\n" % cmd 1450 | log_debug(cmd) 1451 | 1452 | start = datetime.now() 1453 | if gdb_session_view is not None: 1454 | gdb_session_view.add_line(cmd, False) 1455 | gdb_process.stdin.write(cmd.encode(sys.getdefaultencoding())) 1456 | gdb_process.stdin.flush() 1457 | if block: 1458 | countstr = "%d^" % count 1459 | r="" 1460 | while not r.startswith(countstr): 1461 | try: 1462 | r = gdb_lastresult.get(timeout=timeout) 1463 | except queue.Empty: 1464 | raise ValueError("Command \"%s\" took longer than %d seconds to perform?" % (cmd, timeout)) 1465 | return r 1466 | return count 1467 | 1468 | 1469 | def run_python_cmd(cmd, block=False, timeout=None): 1470 | global count 1471 | global gdb_python_command_running 1472 | global gdb_last_console_line 1473 | if not is_running(): 1474 | return "0^error,msg=\"no session running\"" 1475 | 1476 | timeout = timeout or get_setting("gdb_command_timeout", 10) 1477 | 1478 | count = count + 1 1479 | cmd = "%d%s\n" % (count, cmd) 1480 | log_debug(cmd) 1481 | 1482 | start = datetime.now() 1483 | if gdb_session_view is not None: 1484 | gdb_session_view.add_line(cmd, False) 1485 | gdb_last_console_line = "" 1486 | gdb_process.stdin.write(cmd.encode(sys.getdefaultencoding())) 1487 | gdb_process.stdin.flush() 1488 | if block: 1489 | gdb_python_command_running = True 1490 | try: 1491 | countstr = "%d^" % count 1492 | r="" 1493 | while not r.startswith(countstr): 1494 | try: 1495 | r = gdb_lastresult.get(timeout=timeout) 1496 | except queue.Empty: 1497 | raise ValueError("Command \"%s\" took longer than %d seconds to perform?" % (cmd, timeout)) 1498 | return gdb_last_console_line 1499 | finally: 1500 | gdb_python_command_running = False 1501 | return count 1502 | 1503 | 1504 | def wait_until_stopped(): 1505 | if gdb_run_status == "running": 1506 | result = run_cmd("-exec-interrupt --all", True) 1507 | if "^done" in result: 1508 | i = 0 1509 | while not "stopped" in gdb_run_status and i < 100: 1510 | i = i + 1 1511 | time.sleep(0.1) 1512 | if i >= 100: 1513 | log_debug("I'm confused... I think status is %s, but it seems it wasn't..." % gdb_run_status) 1514 | return False 1515 | return True 1516 | return False 1517 | 1518 | 1519 | def resume(): 1520 | global gdb_run_status 1521 | gdb_run_status = "running" 1522 | run_cmd("-exec-continue", True) 1523 | 1524 | 1525 | def get_result(line, show_error = True): 1526 | res = result_regex.search(line).group(0) 1527 | if show_error and res == "error" and not get_setting("i_know_how_to_use_gdb_thank_you_very_much", False): 1528 | sublime.error_message("%s\n\n%s" % (line, "\n".join(traceback.format_stack()))) 1529 | return res 1530 | 1531 | 1532 | def listify(var): 1533 | if not isinstance(var, list): 1534 | return [var] 1535 | return var 1536 | 1537 | 1538 | def update_cursor(): 1539 | global gdb_cursor 1540 | global gdb_cursor_position 1541 | global gdb_stack_index 1542 | global gdb_stack_frame 1543 | 1544 | if not get_setting("update_while_running", True) and gdb_run_status == "running": 1545 | return 1546 | 1547 | res = run_cmd("-stack-info-frame", True) 1548 | if get_result(res) == "error": 1549 | if gdb_run_status != "running": 1550 | log_debug("run_status is %s, but got error: %s" % (gdb_run_status, res)) 1551 | return 1552 | currFrame = parse_result_line(res)["frame"] 1553 | gdb_stack_index = int(currFrame["level"]) 1554 | 1555 | if "fullname" in currFrame: 1556 | gdb_cursor = currFrame["fullname"] 1557 | gdb_cursor_position = int(currFrame["line"]) 1558 | sublime.active_window().focus_group(get_setting("file_group", 0)) 1559 | 1560 | # If gdb_cursor is not the exact name of an already opened file, Sublime 1561 | # Text will open a new view. To prevent that, look through all views to 1562 | # find the file name to use. 1563 | file_to_open = gdb_cursor 1564 | try: 1565 | for view in sublime.active_window().views(): 1566 | if view.file_name(): 1567 | if os.path.samefile(gdb_cursor, view.file_name()): 1568 | file_to_open = view.file_name() 1569 | except Exception: 1570 | pass 1571 | 1572 | sublime.active_window().open_file("%s:%d" % (file_to_open, gdb_cursor_position), sublime.ENCODED_POSITION) 1573 | else: 1574 | gdb_cursor_position = 0 1575 | 1576 | sameFrame = gdb_stack_frame is not None and \ 1577 | gdb_stack_frame["func"] == currFrame["func"] 1578 | if sameFrame and "shlibname" in currFrame and "shlibname" in gdb_stack_frame: 1579 | sameFrame = currFrame["shlibname"] == gdb_stack_frame["shlibname"] 1580 | if sameFrame and "fullname" in currFrame and "fullname" in gdb_stack_frame: 1581 | sameFrame = currFrame["fullname"] == gdb_stack_frame["fullname"] 1582 | 1583 | gdb_stack_frame = currFrame 1584 | # Always need to update the callstack since it's possible to 1585 | # end up in the current function from many different call stacks 1586 | gdb_callstack_view.update_callstack() 1587 | gdb_threads_view.update_threads() 1588 | 1589 | update_view_markers() 1590 | gdb_variables_view.update_variables(sameFrame) 1591 | gdb_register_view.update_values() 1592 | gdb_disassembly_view.update_disassembly() 1593 | 1594 | 1595 | def session_ended_status_message(): 1596 | sublime.status_message("GDB session ended") 1597 | 1598 | 1599 | def gdboutput(pipe): 1600 | global gdb_process 1601 | global gdb_lastresult 1602 | global gdb_lastline 1603 | global gdb_last_console_line 1604 | global gdb_stack_frame 1605 | global gdb_run_status 1606 | global gdb_stack_index 1607 | command_result_regex = re.compile("^\d+\^") 1608 | run_status_regex = re.compile("(^\d*\*)([^,]+)") 1609 | 1610 | while True: 1611 | try: 1612 | raw = pipe.readline() 1613 | if len(raw) == 0: 1614 | log_debug("gdb_%s: broken pipe\n" % ("stdout" if pipe == gdb_process.stdout else "stderr")) 1615 | break 1616 | line = raw.strip().decode(sys.getdefaultencoding()) 1617 | log_debug("gdb_%s: %s\n" % ("stdout" if pipe == gdb_process.stdout else "stderr", line)) 1618 | gdb_session_view.add_line("%s\n" % line, False) 1619 | 1620 | if pipe != gdb_process.stdout: 1621 | continue 1622 | 1623 | if command_result_regex.match(line) is not None: 1624 | gdb_lastresult.put(line) 1625 | 1626 | run_status = run_status_regex.match(line) 1627 | if run_status is not None: 1628 | gdb_run_status = run_status.group(2) 1629 | reason = re.search("(?<=reason=\")[a-zA-Z0-9\-]+(?=\")", line) 1630 | if reason is not None and reason.group(0).startswith("exited"): 1631 | log_debug("gdb: exiting %s" % line) 1632 | run_cmd("-gdb-exit") 1633 | elif not "running" in gdb_run_status and not gdb_shutting_down: 1634 | thread_id = re.search('thread-id="(\d+)"', line) 1635 | if thread_id is not None: 1636 | gdb_threads_view.select_thread(int(thread_id.group(1))) 1637 | sublime.set_timeout(update_cursor, 0) 1638 | if not line.startswith("(gdb)"): 1639 | gdb_lastline = line 1640 | 1641 | if line.startswith("~"): 1642 | console_line = line[2:-1].replace("\\n", "\n").replace("\\\"", "\"").replace("\\t", "\t") 1643 | if not gdb_python_command_running: 1644 | gdb_console_view.add_line(console_line, False) 1645 | 1646 | # save the output (without the newline at the end) 1647 | gdb_last_console_line = console_line[:-1] 1648 | 1649 | # filter out the program output and print it on the console view 1650 | if not (line.startswith("(gdb)") or line.startswith("~") or 1651 | line.startswith("=") or line.startswith("&\"") or 1652 | command_result_regex.match(line) or 1653 | run_status_regex.match(line)): 1654 | # use the raw output to show exactly what the program printed 1655 | console_line = raw.decode(sys.getdefaultencoding()) 1656 | 1657 | # correct the line endings 1658 | console_line = "\n".join(console_line.splitlines()) 1659 | 1660 | gdb_console_view.add_line("%s\n" % console_line, False) 1661 | 1662 | except: 1663 | traceback.print_exc() 1664 | if pipe == gdb_process.stdout: 1665 | log_debug("GDB session ended\n") 1666 | gdb_session_view.add_line("GDB session ended\n") 1667 | sublime.set_timeout(session_ended_status_message, 0) 1668 | gdb_stack_frame = None 1669 | global gdb_cursor_position 1670 | gdb_stack_index = -1 1671 | gdb_cursor_position = 0 1672 | gdb_run_status = None 1673 | sublime.set_timeout(update_view_markers, 0) 1674 | 1675 | for view in gdb_views: 1676 | sublime.set_timeout(view.on_session_ended, 0) 1677 | sublime.set_timeout(cleanup, 0) 1678 | 1679 | def cleanup(): 1680 | global __debug_file_handle 1681 | global gdb_process 1682 | global gdb_threads 1683 | 1684 | # make sure all our threads are done 1685 | for t in gdb_threads: 1686 | t.join(get_setting("gdb_timeout", 20)) 1687 | gdb_threads = [] 1688 | 1689 | # unset the process variable to make sure all pipes and other OS objects are 1690 | # released (this fixes different freezes when gdb is started multiple times) 1691 | gdb_process = None 1692 | 1693 | if get_setting("close_views", True): 1694 | for view in gdb_views: 1695 | view.close() 1696 | if get_setting("push_pop_layout", True): 1697 | gdb_bkp_window.set_layout(gdb_bkp_layout) 1698 | gdb_bkp_window.focus_view(gdb_bkp_view) 1699 | if __debug_file_handle is not None: 1700 | if __debug_file_handle != sys.stdout: 1701 | __debug_file_handle.close() 1702 | __debug_file_handle = None 1703 | 1704 | 1705 | def programio(pty, tty): 1706 | global gdb_process 1707 | exception_count = 0 1708 | class MyFD(object): 1709 | def __init__(self, pty, tty): 1710 | self.pty = pty 1711 | self.tty = tty 1712 | self.off = 0 1713 | self.queue = Queue.Queue() 1714 | 1715 | def on_done(self, s): 1716 | log_debug("programinput: %s\n" % s) 1717 | log_debug("Wrote: %d bytes\n" % os.write(self.pty, bencode("%s\n" % s))) 1718 | os.fsync(self.pty) 1719 | self.queue.put(None) 1720 | 1721 | def get_input(self): 1722 | sublime.active_window().show_input_panel("stdin input expected: ", "input", self.on_done, None, lambda: self.queue.put(None)) 1723 | 1724 | def readline(self): 1725 | ret = bytes() 1726 | while True: 1727 | if not os.isatty(self.pty): 1728 | s = os.fstat(self.pty) 1729 | if self.off >= s.st_size and len(ret) == 0: 1730 | return bdecode(ret) 1731 | else: 1732 | import select 1733 | r, w, x = select.select([self.pty], [self.pty], [], 5.0) 1734 | if len(r) == 0 and len(w) != 0: 1735 | log_debug("Ready for input\n") 1736 | sublime.set_timeout(self.get_input, 0) 1737 | self.queue.get() 1738 | continue 1739 | elif len(r) == 0: 1740 | log_debug("timed out\n") 1741 | break 1742 | read = os.read(self.pty, 1) 1743 | self.off += len(read) 1744 | ret += read 1745 | if len(read) == 0 or ret[-1] == ord('\n'): 1746 | break 1747 | return bdecode(ret) 1748 | 1749 | def close(self): 1750 | os.close(self.pty) 1751 | if self.tty: 1752 | os.close(self.tty) 1753 | 1754 | pipe = MyFD(pty, tty) 1755 | 1756 | while exception_count < 100: 1757 | try: 1758 | line = pipe.readline() 1759 | if len(line) > 0: 1760 | log_debug("programoutput: %s" % line) 1761 | gdb_console_view.add_line(line, False) 1762 | else: 1763 | if gdb_process.poll() is not None: 1764 | break 1765 | time.sleep(0.1) 1766 | except: 1767 | traceback.print_exc() 1768 | exception_count = exception_count + 1 1769 | if pipe is not None: 1770 | pipe.close() 1771 | 1772 | 1773 | gdb_input_view = None 1774 | gdb_command_history = [] 1775 | gdb_command_history_pos = 0 1776 | 1777 | 1778 | def set_input(edit, text): 1779 | gdb_input_view.erase(edit, sublime.Region(0, gdb_input_view.size())) 1780 | gdb_input_view.insert(edit, 0, text) 1781 | 1782 | 1783 | class GdbPrevCmd(sublime_plugin.TextCommand): 1784 | def run(self, edit): 1785 | global gdb_command_history_pos 1786 | if gdb_command_history_pos > 0: 1787 | gdb_command_history_pos -= 1 1788 | if gdb_command_history_pos < len(gdb_command_history): 1789 | set_input(edit, gdb_command_history[gdb_command_history_pos]) 1790 | 1791 | 1792 | class GdbNextCmd(sublime_plugin.TextCommand): 1793 | def run(self, edit): 1794 | global gdb_command_history_pos 1795 | if gdb_command_history_pos < len(gdb_command_history): 1796 | gdb_command_history_pos += 1 1797 | if gdb_command_history_pos < len(gdb_command_history): 1798 | set_input(edit, gdb_command_history[gdb_command_history_pos]) 1799 | else: 1800 | set_input(edit, "") 1801 | 1802 | 1803 | def show_input(raw=False): 1804 | global gdb_input_view 1805 | global gdb_command_history_pos 1806 | gdb_command_history_pos = len(gdb_command_history) 1807 | 1808 | title = "GDB" 1809 | if raw: 1810 | title += " Raw" 1811 | 1812 | gdb_input_view = sublime.active_window().show_input_panel( 1813 | title, "", 1814 | partial(input_on_done, raw=raw), 1815 | input_on_change, 1816 | input_on_cancel) 1817 | 1818 | 1819 | def input_on_done(s, raw=False): 1820 | if s.strip() != "quit": 1821 | gdb_command_history.append(s) 1822 | show_input(raw=raw) 1823 | 1824 | mimode = not raw 1825 | run_cmd(s, mimode=mimode) 1826 | 1827 | 1828 | def input_on_cancel(): 1829 | pass 1830 | 1831 | 1832 | def input_on_change(s): 1833 | pass 1834 | 1835 | 1836 | def is_running(): 1837 | return gdb_process is not None and gdb_process.poll() is None 1838 | 1839 | 1840 | class GdbInput(sublime_plugin.WindowCommand): 1841 | def run(self): 1842 | show_input(raw=False) 1843 | 1844 | 1845 | class GdbRawInput(sublime_plugin.WindowCommand): 1846 | def run(self): 1847 | show_input(raw=True) 1848 | 1849 | 1850 | class GdbLaunch(sublime_plugin.WindowCommand): 1851 | def run(self): 1852 | global exec_settings 1853 | s = self.window.active_view().settings() 1854 | exec_choices = s.get("sublimegdb_executables") 1855 | 1856 | if exec_choices is None or type(exec_choices) != dict: 1857 | # No executable specific settings, go ahead and launch 1858 | global gdb_threads 1859 | exec_settings = {} 1860 | t = threading.Thread(target=self.launch) 1861 | t.start() 1862 | gdb_threads.append(t) 1863 | return 1864 | 1865 | def on_choose(index): 1866 | global exec_settings 1867 | global gdb_threads 1868 | if index == -1: 1869 | # User cancelled the panel, abort launch 1870 | return 1871 | exec_name = list(exec_choices)[index] 1872 | exec_settings = exec_choices[exec_name] 1873 | t = threading.Thread(target=self.launch) 1874 | t.start() 1875 | gdb_threads.append(t) 1876 | 1877 | self.window.show_quick_panel(list(exec_choices), on_choose) 1878 | 1879 | def launch(self): 1880 | global gdb_process 1881 | global gdb_server_process 1882 | global gdb_threads 1883 | global gdb_run_status 1884 | global gdb_bkp_window 1885 | global gdb_bkp_view 1886 | global gdb_bkp_layout 1887 | global gdb_shutting_down 1888 | global DEBUG 1889 | global DEBUG_FILE 1890 | view = self.window.active_view() 1891 | DEBUG = get_setting("debug", False, view) 1892 | DEBUG_FILE = expand_path(get_setting("debug_file", "stdout", view), self.window) 1893 | if DEBUG: 1894 | log_debug("Will write debug info to file: %s" % DEBUG_FILE) 1895 | if gdb_process is None or gdb_process.poll() is not None: 1896 | commandline = get_setting("commandline", view=view) 1897 | if isinstance(commandline, list): 1898 | # backwards compatibility for when the commandline was a list 1899 | commandline = " ".join(commandline) 1900 | # note path expanding happens before add/switch view 1901 | commandline = expand_path(commandline, self.window) 1902 | path = expand_path(get_setting("workingdir", "/tmp", view), self.window) 1903 | arguments = expand_path(get_setting("arguments", ""), self.window) 1904 | log_debug("Running: %s\n" % commandline) 1905 | log_debug("In directory: %s\n" % path) 1906 | if commandline == "notset" or path == "notset": 1907 | sublime.error_message("You have not configured the plugin correctly, the default configuration file and your user configuration file will open in a new window") 1908 | sublime.run_command("new_window") 1909 | wnd = sublime.active_window() 1910 | wnd.set_layout({ 1911 | "cols": [0.0, 0.5, 1.0], 1912 | "rows": [0, 1.0], 1913 | "cells": [[0,0,1,1], [1,0,2,1]], 1914 | }) 1915 | v = wnd.open_file("%s/User/SublimeGDB.sublime-settings" % sublime.packages_path()) 1916 | v2 = wnd.open_file("%s/SublimeGDB/SublimeGDB.sublime-settings" % sublime.packages_path()) 1917 | wnd.set_view_index(v2, 1, 0) 1918 | return 1919 | if not os.path.exists(path): 1920 | sublime.error_message("The directory given does not exist: %s" % path) 1921 | return 1922 | 1923 | # get env settings 1924 | gdb_env = get_setting("env", "notset") 1925 | if gdb_env == "notset": 1926 | gdb_env = None 1927 | else: 1928 | env_copy = os.environ.copy() 1929 | env_copy.update(gdb_env) 1930 | gdb_env = env_copy 1931 | 1932 | # Optionally Launch the GDB Server 1933 | gdb_server_cmd = get_setting("server_commandline", "notset") 1934 | gdb_server_dir = get_setting("server_workingdir", "notset") 1935 | if (gdb_server_cmd != "notset") and (gdb_server_dir != "notset"): 1936 | 1937 | gdb_server_cmd = expand_path(gdb_server_cmd, self.window) 1938 | gdb_server_dir = expand_path(gdb_server_dir, self.window) 1939 | gdb_server_shell = get_setting("server_shell", False) 1940 | log_debug("gdb_server_cmd: %s" % gdb_server_cmd) 1941 | log_debug("gdb_server_dir: %s" % gdb_server_dir) 1942 | log_debug("gdb_server_dir: %s" % gdb_server_shell) 1943 | gdb_server_process = subprocess.Popen(gdb_server_cmd, shell=gdb_server_shell, cwd=gdb_server_dir, env=gdb_env) 1944 | 1945 | 1946 | gdb_process = subprocess.Popen(commandline, shell=True, cwd=path, env=gdb_env, 1947 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1948 | 1949 | log_debug("Process: %s\n" % gdb_process) 1950 | gdb_bkp_window = sublime.active_window() 1951 | #back up current layout before opening the debug one 1952 | #it will be restored when debug is finished 1953 | gdb_bkp_layout = gdb_bkp_window.get_layout() 1954 | gdb_bkp_view = gdb_bkp_window.active_view() 1955 | gdb_bkp_window.set_layout( 1956 | get_setting("layout", 1957 | { 1958 | "cols": [0.0, 0.5, 1.0], 1959 | "rows": [0.0, 0.75, 1.0], 1960 | "cells": [[0, 0, 2, 1], [0, 1, 1, 2], [1, 1, 2, 2]] 1961 | } 1962 | ) 1963 | ) 1964 | 1965 | for view in gdb_views: 1966 | if view.is_closed() and view.open_at_start(): 1967 | view.open() 1968 | view.clear() 1969 | 1970 | gdb_shutting_down = False 1971 | 1972 | t = threading.Thread(target=gdboutput, args=(gdb_process.stdout,)) 1973 | t.start() 1974 | gdb_threads.append(t) 1975 | t = threading.Thread(target=gdboutput, args=(gdb_process.stderr,)) 1976 | t.start() 1977 | gdb_threads.append(t) 1978 | 1979 | try: 1980 | raise Exception("Nope") 1981 | pty, tty = os.openpty() 1982 | name = os.ttyname(tty) 1983 | except: 1984 | pipe, name = tempfile.mkstemp() 1985 | pty, tty = pipe, None 1986 | log_debug("pty: %s, tty: %s, name: %s" % (pty, tty, name)) 1987 | t = threading.Thread(target=programio, args=(pty,tty)) 1988 | t.start() 1989 | gdb_threads.append(t) 1990 | try: 1991 | run_cmd("-gdb-show interpreter", True, timeout=get_setting("gdb_timeout", 20)) 1992 | except: 1993 | sublime.error_message("""\ 1994 | It seems you're not running gdb with the "mi" interpreter. Please add 1995 | "--interpreter=mi" to your gdb command line""") 1996 | gdb_process.stdin.write("quit\n") 1997 | return 1998 | run_cmd("-inferior-tty-set %s" % name, True) 1999 | 2000 | run_cmd("-enable-pretty-printing") 2001 | run_cmd("-gdb-set mi-async on") 2002 | run_cmd("-gdb-set pagination off") 2003 | dis_asm_flavor = get_setting("disassembly_flavor", "att", view) 2004 | if dis_asm_flavor == "intel": 2005 | run_cmd("-gdb-set disassembly-flavor intel") 2006 | else: 2007 | run_cmd("-gdb-set disassembly-flavor att") 2008 | # if gdb_nonstop: 2009 | # run_cmd("-gdb-set non-stop on") 2010 | attach_cmd = get_setting("attach_cmd","notset") 2011 | if(attach_cmd != "notset"): 2012 | run_cmd(attach_cmd, block=True, timeout=get_setting("gdb_timeout", 20)) 2013 | 2014 | gdb_breakpoint_view.sync_breakpoints() 2015 | 2016 | if(get_setting("run_after_init", True)): 2017 | gdb_run_status = "running" 2018 | if arguments: 2019 | run_cmd("-exec-arguments " + arguments) 2020 | 2021 | run_cmd(get_setting("exec_cmd", "-exec-run"), True) 2022 | else: 2023 | gdb_run_status = "stopped" 2024 | 2025 | if(get_setting("enable_pretty_printing", True)): 2026 | run_cmd("-enable-pretty-printing") 2027 | 2028 | show_input() 2029 | 2030 | else: 2031 | sublime.status_message("GDB is already running!") 2032 | 2033 | def is_enabled(self): 2034 | return not is_running() 2035 | 2036 | def is_visible(self): 2037 | return not is_running() 2038 | 2039 | 2040 | class GdbContinue(sublime_plugin.WindowCommand): 2041 | def run(self): 2042 | global gdb_cursor_position 2043 | gdb_cursor_position = 0 2044 | update_view_markers() 2045 | resume() 2046 | 2047 | def is_enabled(self): 2048 | return is_running() and gdb_run_status != "running" 2049 | 2050 | def is_visible(self): 2051 | return is_running() 2052 | 2053 | 2054 | class GdbExit(sublime_plugin.WindowCommand): 2055 | def run(self): 2056 | global gdb_shutting_down 2057 | gdb_shutting_down = True 2058 | wait_until_stopped() 2059 | run_cmd("-gdb-exit", True) 2060 | if gdb_server_process: 2061 | gdb_server_process.terminate() 2062 | 2063 | def is_enabled(self): 2064 | return is_running() 2065 | 2066 | def is_visible(self): 2067 | return is_running() 2068 | 2069 | class GdbLoad(sublime_plugin.WindowCommand): 2070 | def run(self): 2071 | run_cmd(get_setting("load_cmd", "-target-download")) 2072 | 2073 | def is_enabled(self): 2074 | return is_running() and gdb_run_status != "running" 2075 | 2076 | def is_visible(self): 2077 | return is_running() and gdb_run_status != "running" 2078 | 2079 | class GdbPause(sublime_plugin.WindowCommand): 2080 | def run(self): 2081 | run_cmd("-exec-interrupt") 2082 | 2083 | def is_enabled(self): 2084 | return is_running() and gdb_run_status != "stopped" 2085 | 2086 | def is_visible(self): 2087 | return is_running() and gdb_run_status != "stopped" 2088 | 2089 | 2090 | class GdbStepOver(sublime_plugin.WindowCommand): 2091 | def run(self): 2092 | run_cmd("-exec-next") 2093 | 2094 | def is_enabled(self): 2095 | return is_running() and gdb_run_status != "running" 2096 | 2097 | def is_visible(self): 2098 | return is_running() 2099 | 2100 | 2101 | class GdbStepInto(sublime_plugin.WindowCommand): 2102 | def run(self): 2103 | run_cmd("-exec-step") 2104 | 2105 | def is_enabled(self): 2106 | return is_running() and gdb_run_status != "running" 2107 | 2108 | def is_visible(self): 2109 | return is_running() 2110 | 2111 | 2112 | class GdbNextInstruction(sublime_plugin.WindowCommand): 2113 | def run(self): 2114 | run_cmd("-exec-next-instruction") 2115 | 2116 | def is_enabled(self): 2117 | return is_running() and gdb_run_status != "running" 2118 | 2119 | def is_visible(self): 2120 | return is_running() 2121 | 2122 | 2123 | class GdbStepOut(sublime_plugin.WindowCommand): 2124 | def run(self): 2125 | run_cmd("-exec-finish") 2126 | 2127 | def is_enabled(self): 2128 | return is_running() and gdb_run_status != "running" 2129 | 2130 | def is_visible(self): 2131 | return is_running() 2132 | 2133 | 2134 | class GdbAddWatch(sublime_plugin.TextCommand): 2135 | def run(self, edit): 2136 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2137 | var = gdb_variables_view.get_variable_at_line(self.view.rowcol(self.view.sel()[0].begin())[0]) 2138 | if var is not None: 2139 | gdb_breakpoint_view.toggle_watch(var.get_expression()) 2140 | else: 2141 | sublime.status_message("Don't know how to watch that variable") 2142 | else: 2143 | exp = self.view.substr(self.view.word(self.view.sel()[0].begin())) 2144 | gdb_breakpoint_view.toggle_watch(exp) 2145 | 2146 | 2147 | class GdbToggleBreakpoint(sublime_plugin.TextCommand): 2148 | def run(self, edit): 2149 | fn = self.view.file_name() 2150 | 2151 | if gdb_breakpoint_view.is_open() and self.view.id() == gdb_breakpoint_view.get_view().id(): 2152 | row = self.view.rowcol(self.view.sel()[0].begin())[0] 2153 | if row < len(gdb_breakpoint_view.breakpoints): 2154 | gdb_breakpoint_view.breakpoints[row].remove() 2155 | gdb_breakpoint_view.breakpoints.pop(row) 2156 | gdb_breakpoint_view.update_view() 2157 | elif gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2158 | var = gdb_variables_view.get_variable_at_line(self.view.rowcol(self.view.sel()[0].begin())[0]) 2159 | if var is not None: 2160 | gdb_breakpoint_view.toggle_watch(var.get_expression()) 2161 | elif gdb_disassembly_view.is_open() and self.view.id() == gdb_disassembly_view.get_view().id(): 2162 | for sel in self.view.sel(): 2163 | line = self.view.substr(self.view.line(sel)) 2164 | addr = re.match(r"^[^:]+", line) 2165 | if addr: 2166 | gdb_breakpoint_view.toggle_breakpoint_addr(addr.group(0)) 2167 | elif fn is not None: 2168 | for sel in self.view.sel(): 2169 | line, col = self.view.rowcol(sel.a) 2170 | gdb_breakpoint_view.toggle_breakpoint(fn, line + 1) 2171 | update_view_markers(self.view) 2172 | 2173 | 2174 | class GdbClick(sublime_plugin.TextCommand): 2175 | def run(self, edit): 2176 | if not is_running(): 2177 | return 2178 | 2179 | row, col = self.view.rowcol(self.view.sel()[0].a) 2180 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2181 | gdb_variables_view.expand_collapse_variable(self.view, toggle=True) 2182 | elif gdb_callstack_view.is_open() and self.view.id() == gdb_callstack_view.get_view().id(): 2183 | gdb_callstack_view.select(row) 2184 | elif gdb_threads_view.is_open() and self.view.id() == gdb_threads_view.get_view().id(): 2185 | gdb_threads_view.select(row) 2186 | update_cursor() 2187 | 2188 | def is_enabled(self): 2189 | return is_running() 2190 | 2191 | 2192 | class GdbDoubleClick(sublime_plugin.TextCommand): 2193 | def run(self, edit): 2194 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2195 | self.view.run_command("gdb_edit_variable") 2196 | else: 2197 | self.view.run_command("gdb_edit_register") 2198 | 2199 | def is_enabled(self): 2200 | return is_running() and \ 2201 | ((gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id()) or \ 2202 | (gdb_register_view.is_open() and self.view.id() == gdb_register_view.get_view().id())) 2203 | 2204 | 2205 | class GdbCollapseVariable(sublime_plugin.TextCommand): 2206 | def run(self, edit): 2207 | gdb_variables_view.expand_collapse_variable(self.view, expand=False) 2208 | 2209 | def is_enabled(self): 2210 | if not is_running(): 2211 | return False 2212 | row, col = self.view.rowcol(self.view.sel()[0].a) 2213 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2214 | return True 2215 | return False 2216 | 2217 | 2218 | class GdbExpandVariable(sublime_plugin.TextCommand): 2219 | def run(self, edit): 2220 | gdb_variables_view.expand_collapse_variable(self.view) 2221 | 2222 | def is_enabled(self): 2223 | if not is_running(): 2224 | return False 2225 | row, col = self.view.rowcol(self.view.sel()[0].a) 2226 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2227 | return True 2228 | return False 2229 | 2230 | 2231 | class GdbEditVariable(sublime_plugin.TextCommand): 2232 | def run(self, edit): 2233 | row, col = self.view.rowcol(self.view.sel()[0].a) 2234 | var = gdb_variables_view.get_variable_at_line(row) 2235 | if var.is_editable(): 2236 | var.edit() 2237 | else: 2238 | sublime.status_message("Variable isn't editable") 2239 | 2240 | def is_enabled(self): 2241 | if not is_running(): 2242 | return False 2243 | if gdb_variables_view.is_open() and self.view.id() == gdb_variables_view.get_view().id(): 2244 | return True 2245 | return False 2246 | 2247 | 2248 | class GdbEditRegister(sublime_plugin.TextCommand): 2249 | def run(self, edit): 2250 | row, col = self.view.rowcol(self.view.sel()[0].a) 2251 | reg = gdb_register_view.get_register_at_line(row) 2252 | if not reg is None: 2253 | reg.edit() 2254 | 2255 | def is_enabled(self): 2256 | if not is_running(): 2257 | return False 2258 | if gdb_register_view.is_open() and self.view.id() == gdb_register_view.get_view().id(): 2259 | return True 2260 | return False 2261 | 2262 | 2263 | class GdbEventListener(sublime_plugin.EventListener): 2264 | def on_query_context(self, view, key, operator, operand, match_all): 2265 | if key == "gdb_running": 2266 | return is_running() == operand 2267 | elif key == "gdb_input_view": 2268 | return gdb_input_view is not None and view.id() == gdb_input_view.id() 2269 | elif key.startswith("gdb_"): 2270 | v = gdb_variables_view 2271 | if key.startswith("gdb_register_view"): 2272 | v = gdb_register_view 2273 | elif key.startswith("gdb_disassembly_view"): 2274 | v = gdb_disassembly_view 2275 | if key.endswith("open"): 2276 | return v.is_open() == operand 2277 | else: 2278 | if v.get_view() is None: 2279 | return False == operand 2280 | return (view.id() == v.get_view().id()) == operand 2281 | return None 2282 | 2283 | def on_activated(self, view): 2284 | if view.file_name() is not None: 2285 | update_view_markers(view) 2286 | 2287 | # forward this event to GDBView 2288 | if view.name(): 2289 | for gdb_view in gdb_views: 2290 | if view.name() == gdb_view.name: 2291 | gdb_view.on_activated() 2292 | break 2293 | 2294 | def on_modified(self, view): 2295 | # update the current breakpoint locations 2296 | gdb_breakpoint_view.on_view_modified(view) 2297 | 2298 | def on_load(self, view): 2299 | if view.file_name() is not None: 2300 | update_view_markers(view) 2301 | 2302 | def on_close(self, view): 2303 | for v in gdb_views: 2304 | if v.is_open() and view.id() == v.get_view().id(): 2305 | v.was_closed() 2306 | break 2307 | 2308 | 2309 | class GdbOpenSessionView(sublime_plugin.WindowCommand): 2310 | def run(self): 2311 | gdb_session_view.open() 2312 | 2313 | def is_enabled(self): 2314 | return not gdb_session_view.is_open() 2315 | 2316 | def is_visible(self): 2317 | return not gdb_session_view.is_open() 2318 | 2319 | 2320 | class GdbOpenConsoleView(sublime_plugin.WindowCommand): 2321 | def run(self): 2322 | gdb_console_view.open() 2323 | 2324 | def is_enabled(self): 2325 | return not gdb_console_view.is_open() 2326 | 2327 | def is_visible(self): 2328 | return not gdb_console_view.is_open() 2329 | 2330 | 2331 | class GdbOpenVariablesView(sublime_plugin.WindowCommand): 2332 | def run(self): 2333 | gdb_variables_view.open() 2334 | 2335 | def is_enabled(self): 2336 | return not gdb_variables_view.is_open() 2337 | 2338 | def is_visible(self): 2339 | return not gdb_variables_view.is_open() 2340 | 2341 | 2342 | class GdbOpenCallstackView(sublime_plugin.WindowCommand): 2343 | def run(self): 2344 | gdb_callstack_view.open() 2345 | 2346 | def is_enabled(self): 2347 | return not gdb_callstack_view.is_open() 2348 | 2349 | def is_visible(self): 2350 | return not gdb_callstack_view.is_open() 2351 | 2352 | 2353 | class GdbOpenRegisterView(sublime_plugin.WindowCommand): 2354 | def run(self): 2355 | gdb_register_view.open() 2356 | 2357 | def is_enabled(self): 2358 | return not gdb_register_view.is_open() 2359 | 2360 | def is_visible(self): 2361 | return not gdb_register_view.is_open() 2362 | 2363 | 2364 | class GdbOpenDisassemblyView(sublime_plugin.WindowCommand): 2365 | def run(self): 2366 | gdb_disassembly_view.open() 2367 | 2368 | def is_enabled(self): 2369 | return not gdb_disassembly_view.is_open() 2370 | 2371 | def is_visible(self): 2372 | return not gdb_disassembly_view.is_open() 2373 | 2374 | 2375 | class GdbOpenBreakpointView(sublime_plugin.WindowCommand): 2376 | def run(self): 2377 | gdb_breakpoint_view.open() 2378 | 2379 | def is_enabled(self): 2380 | return not gdb_breakpoint_view.is_open() 2381 | 2382 | def is_visible(self): 2383 | return not gdb_breakpoint_view.is_open() 2384 | 2385 | 2386 | class GdbOpenThreadsView(sublime_plugin.WindowCommand): 2387 | def run(self): 2388 | gdb_threads_view.open() 2389 | 2390 | def is_enabled(self): 2391 | return not gdb_threads_view.is_open() 2392 | 2393 | def is_visible(self): 2394 | return not gdb_threads_view.is_open() 2395 | 2396 | --------------------------------------------------------------------------------