├── DESIGN.txt ├── Makefile ├── README.md ├── __README__.txt ├── autoexp.dat ├── calctax.pl ├── cpp1.cpp ├── demo ├── __VGDB_DEMO__.htm ├── __VGDB_DEMO__.js ├── __VGDB_DEMO__.swf ├── __VGDB_DEMO_v2.htm ├── __VGDB_DEMO_v2.js ├── __VGDB_DEMO_v2.swf └── screenshot.png ├── libvgdbc.so ├── vgdb ├── vgdb.bat ├── vgdb.vim ├── vgdb_install ├── vgdb_install_mswin.bat ├── vgdbc.c └── vgdbc.dll /DESIGN.txt: -------------------------------------------------------------------------------- 1 | *) vgdb parameters 2 | vgdb [-vgdb-{env}] [-vi|-c] {target and parameters} 3 | 4 | -vgdb-{env} will overwritten envvar named "VGDB_{env}", e.g. 5 | export VGDB_DEBUG=1 6 | export VGDB_PORT=33089 7 | vgdb cpp1 8 | => 9 | vgdb -vgdb-debug -vgdb-port:33333 cpp1 10 | -vi 11 | a mark that indicates it's called by vi; ignore to start vi 12 | -c 13 | client mode 14 | 15 | *) Debug vgdb via log: 16 | 17 | Start vgdb in command mode: 18 | export VGDB_PORT=44444 19 | export VGDB_DEBUG=1 20 | vgdb -vi cpp1 21 | or 22 | vgdb -vgdb-debug -vgdb-port:44444 -vi cpp1 23 | (set VGDB_DEBUG to use the fix port) 24 | 25 | Command from client side: 26 | export VGDB_PORT=44444 27 | vgdb -c ".init" 28 | vgdb -c "b main; r; n" 29 | vgdb -c ".p m" 30 | vgdb -c "q" 31 | 32 | *) Debug vgdb via vgdb 33 | Start vgdb in command mode: 34 | vgdb -vgdb-debug vgdb -vgdb-port:44444 -vi cpp1 35 | 36 | Note: 37 | - outer vgdb debug the perl program 'vgdb' using a random port (as it's not specified in envvar or argument) 38 | - inner vgdb debug the C++ program 'cpp1' using port 44444 39 | - Note: inner env will inherit outer env (e.g. -vgdb-debug is inheritted by inner) 40 | 41 | *) Abnormal quit or killed 42 | if no response from vgdb or vgdb is forcely killed, then it may not run, please try: 43 | kill process of: perl gdb vgdb gvim 44 | find and kill the program occupied the vgdb port: 45 | netstat -anp | grep 30899 46 | (on windows, use "netstat -ano") 47 | kill {pid} 48 | 49 | ("netstat -p" is to show process id (pid) using the port, on MS Windows use "-o" instead) 50 | 51 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CXX=g++ 3 | VER=1.3a 4 | 5 | ifdef windir 6 | 7 | CFLAGS=-g -D_WINDOWS 8 | CXXFLAGS=$(CFLAGS) 9 | 10 | all: vgdbc.dll vgdbc_test.exe cpp1.exe 11 | 12 | test: all 13 | gvim -c "echo libcall('vgdbc.dll', 'test', 'libcall test')" 14 | 15 | test2: vgdbc_test.exe 16 | @$< "help" 17 | 18 | vgdbc_test.exe vgdbc.dll: LDFLAGS=-lwsock32 19 | 20 | vgdbc_test.exe: vgdbc.o vgdbc.dll 21 | $(CC) $^ -o $@ $(LDFLAGS) 22 | 23 | vgdbc.dll: vgdbc.o 24 | $(CC) -shared $^ -o $@ $(LDFLAGS) 25 | 26 | clean: 27 | -rm -rf *.o vgdbc.dll vgdbc_test.exe 28 | 29 | cpp1.exe: cpp1.cpp Makefile 30 | $(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS) 31 | 32 | else # for linux: 33 | 34 | CFLAGS=-g -fPIC -D_LINUX 35 | CXXFLAGS=$(CFLAGS) 36 | LDFLAGS=-ldl 37 | 38 | all: libvgdbc.so vgdbc_test cpp1 39 | 40 | test: all 41 | export LD_LIBRARY_PATH=`pwd` && vi -c "echo libcall('libvgdbc.so', 'test', 'libcall test')" 42 | 43 | test2: vgdbc_test 44 | @$< "help" 45 | 46 | vgdbc_test: vgdbc.o 47 | $(CC) $^ -o $@ $(LDFLAGS) 48 | 49 | libvgdbc.so: vgdbc.o 50 | $(CC) -shared $^ -o $@ $(LDFLAGS) 51 | 52 | clean: 53 | -rm -rf *.o libvgdbc.so vgdbc_test 54 | 55 | cpp1: cpp1.cpp Makefile 56 | $(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS) 57 | 58 | endif 59 | 60 | NAME=vgdb-$(VER) 61 | DIST=$(NAME).tgz 62 | 63 | dist: 64 | git archive --format tgz --output $(DIST) --prefix=$(NAME)/ HEAD 65 | @echo $(DIST) generated. 66 | 67 | ###### 68 | vgdbc.o: vgdbc.c Makefile 69 | 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vgdb 2 | 3 | VGdb is a perl application that enables vim as the front end of a debugger 4 | like gdb and perldb. 5 | It both works on MS Windows and Linux. It both works in gvim and vim. 6 | 7 | It opens a VGDB window in vim that allows user to type gdb command directly, 8 | and gdb output is redirected to this windows. 9 | 10 | MSVC-style shortcuts are defined by default (like F5/F10...), and you can 11 | define how to preview structure like MSVC auto expand feature. 12 | 13 | Screenshot: 14 | ![Screenshot](https://github.com/skyshore2001/vgdb-vim/raw/master/demo/screenshot.png) 15 | 16 | A flash demo in my package helps you quickly go through the vgdb features. 17 | 18 | **For detail, read this vim help document: __README__.txt** 19 | 20 | ## Install 21 | 22 | On Linux, run vgdb_install and specify path. 23 | 24 | # sh vgdb_install 25 | 26 | On MS Windows, you need install Perl (and of course gcc/gdb). 27 | Run vgdb_install_mswin.bat that actually copy files to your folder. 28 | 29 | Note: 30 | - gvim MUST in the default search path. 31 | 32 | My dev environment: 33 | - Windows: 34 | - Perl 5.8.8 MSWin32-x86 35 | - Gdb 7.4 i686-pc-mingw32 36 | - Linux: 37 | - Perl 5.10.0 for x86_64-linux 38 | - Gdb 7.3 x86_64-suse-linux 39 | 40 | ## Quick usage 41 | 42 | In vim or gvim, run :VGdb command, e.g. 43 | 44 | :VGdb 45 | :VGdb cpp1 46 | :VGdb cpp1 --tty=/dev/pts/4 47 | 48 | To debug a perl program: 49 | 50 | :VGdb hello.pl 51 | 52 | The following shortcuts is applied that is similar to MSVC: 53 | 54 | - run or continue 55 | - stop debugging (kill) 56 | - next 57 | - step into 58 | - step out (finish) 59 | - run to cursor (tb and c) 60 | - toggle breakpoint on current line 61 | - toggle enable/disable breakpoint on current line 62 | \ju or - set next statement (tb and jump) 63 | - view variable under the cursor (.p) 64 | 65 | -------------------------------------------------------------------------------- /__README__.txt: -------------------------------------------------------------------------------- 1 | *vgdb.txt* For Vim version 7.3. Last change: 2013 Feb 22 2 | 3 | VGdb - Visual Gdb in VIM, v1 4 | Liang, Jian - 2013/1 5 | 6 | 1. Introduction |vgdb| 7 | 2. Install |vgdb-install| 8 | 3. Start vgdb |:VGdb| 9 | 4. Using vgdb |vgdb-using| 10 | 5. Preview variable (auto expand) |vgdb-preview| 11 | 6. VGdb client |vgdbc| 12 | 7. Contact me |vgdb-contact| 13 | 14 | ============================================================================== 15 | 1. Introduction *vgdb* 16 | 17 | VGdb is a perl application that enables vim as the front end of a debugger 18 | like gdb and perldb. 19 | It both works on MS Windows and Linux. It both works in gvim and vim. 20 | 21 | It opens a VGDB window in vim that allows user to type gdb command directly, 22 | and gdb output is redirected to this windows. 23 | 24 | MSVC-style shortcuts are defined by default (like F5/F10...), and you can 25 | define how to preview structure like MSVC auto expand feature. 26 | 27 | A flash demo in my package helps you quickly go through the vgdb features. 28 | 29 | ============================================================================== 30 | 2. Install *vgdb-install* 31 | 32 | On Linux, run vgdb_install and specify path. > 33 | # sh vgdb_install 34 | 35 | On MS Windows, you need install Perl (and of course gcc/gdb). 36 | Run vgdb_install_mswin.bat that actually copy files to your folder. 37 | 38 | Note: 39 | gvim MUST in the default search path. 40 | 41 | My dev environment: 42 | Windows: 43 | Perl 5.8.8 MSWin32-x86 44 | Gdb 7.4 i686-pc-mingw32 45 | 46 | Linux: 47 | Perl 5.10.0 for x86_64-linux 48 | Gdb 7.3 x86_64-suse-linux 49 | 50 | ============================================================================== 51 | 3. Start vgdb *:VGdb* 52 | 53 | * method 1: in vim or gvim, run :VGdb command, e.g. > 54 | 55 | :VGdb 56 | :VGdb cpp1 57 | :VGdb cpp1 --tty=/dev/pts/4 58 | 59 | You can directly type "gdb" in vi command line as it's set to be the abbreviation of VGdb: > 60 | 61 | :gdb 62 | 63 | To debug a perl program: > 64 | 65 | :VGdb hello.pl 66 | 67 | vgdb identifies the target file ('hello.pl') is perl application and starts 68 | perl debugger ('perl -d') internally. 69 | 70 | * method 2: Run vgdb (or vgdb.bat on Windows) 71 | it open gvim with the __VGDB__ window for gdb command: > 72 | 73 | $ vgdb 74 | $ vgdb cpp1 75 | $ vgdb hello.pl 76 | 77 | ============================================================================== 78 | 4. Using vgdb *vgdb-using* 79 | 80 | A flash demo (__VGDB_DEMO__.htm) in my package helps you quickly go through the 81 | vgdb features. Here are how you can use vgdb: 82 | 83 | 1) Type gdb command in the __VGDB__ window. Some commands starting with '.' 84 | are known as "vgdb" commands: > 85 | 86 | .c - run or continue 87 | q - quit gdb and vgdb 88 | .p {var} - preview var (according to autoexp.dat); shortcut 89 | .debug={0|1} - disable|enable to show debug info in vgdb 90 | .ver - show vgdb version info 91 | .ju {pos} - jump to position (auto tbreak and allow cross-function) 92 | 93 | Such vgdb command is used by vgdb internally, don't run it manually! > 94 | 95 | .init - get the init message 96 | 97 | 2) Press Enter or double click in the __VGDB__ window (vim normal mode). 98 | 99 | 2.1) Jump to a code point or breakpoint: 100 | if a line contains file/lineno like > 101 | AddMoney (money=0x7fffffffde20, d=1.1399999999999999) at cpp1.cpp:24 102 | OR > 103 | breakpoint 2 at 0x4009ad: file cpp1.cpp, line 77. 104 | you can press enter on these lines to go there. Such lines are often 105 | highlighted. 106 | 107 | 2.2) Jump to a frame of the call stack: 108 | show all frames: > 109 | bt (or where) 110 | it shows frames like this > 111 | #0 ... 112 | #1 ... 113 | double click or press on the #xx line, it directly goto the frame. 114 | 115 | 2.3) Dereference a pointer 116 | A pointer variable is only shown like this: > 117 | $1 = (SBOString *) 0x7fffffffde10 118 | press enter on this line will get the content just like you run > 119 | .p *$1 120 | 121 | So do struct/class members of pointer type: > 122 | $2 = { 123 | m_strData = 0x603010 124 | } 125 | press enter on the line of "m_strData" to show this member instead of pointer. 126 | 127 | 2.4) Thread switch 128 | show all threads: > 129 | i thr 130 | it show threads like this: > 131 | 4 Thread 0x7fc53b3cf700 (LWP 25792) ... from /lib64/libpthread.so.0 132 | 3 Thread 0x7fc53b2ce700 (LWP 25793) ... from /lib64/libpthread.so.0 133 | * 1 Thread 0x7fc54fa8e700 (LWP 25766) ... from /lib64/libpthread.so.0 134 | double click or press on the line, it switch to this thread and show the 135 | call stack. 136 | 137 | 3) Shortcuts 138 | 139 | The following shortcuts is applied that is similar to MSVC: 140 | 141 | - run or continue (.c) 142 | - stop debugging (kill) 143 | - next 144 | - step into 145 | - step out (finish) 146 | - run to cursor (tb and c) 147 | - toggle breakpoint on current line 148 | - toggle enable/disable breakpoint on current line 149 | \ju or - set next statement (tb and jump) 150 | - view variable under the cursor (.p) 151 | 152 | Note: 153 | If you use vgdb in the vim in a gnome terminal, the may be conflict with the 154 | terminal shortcut. You can find menu Edit->Keyboard shortcuts... to disable in 155 | the terminal. 156 | 157 | All shortcuts will be restored after you quit vgdb. 158 | 159 | 4) Preview variable via mouse in gvim 160 | 161 | The balloon event is supported that you can move you mouse on a variable to 162 | preview the value. It's the same as you input "p var" command or press 163 | on the variable. 164 | 165 | 5) VGdb command 166 | 167 | VGdb command has the same effect as you input in the __VGDB__ window. e.g. > 168 | 169 | :VGdb next 170 | 171 | 6) Execution point jump 172 | 173 | VGdb provides simple jump by pressing "\ju" on the new position. More, it 174 | supports cross-function jump. Here's an example: > 175 | void Fun1() 176 | { 177 | .. Fun2(); 178 | printf("OK\n"); // <- you want come there directly 179 | } 180 | void Fun2() 181 | { 182 | => printf("Fun2\n"); // current execution position 183 | ... 184 | } 185 | 186 | It is in Fun2 but you want to directly jump back to the caller Fun1() and 187 | continue running. You first switch the frame into Fun1, e.g. > 188 | :VGdb up 189 | Then move the cursor to the new line and press "\ju". VGdb will fix the stack 190 | besides jumping. 191 | 192 | 7) Pause the execution 193 | 194 | As you know you can press in gdb to pause the execution and then 195 | inspect. But in vgdb, when the target program is running, you cannot get control 196 | in vim. Instead, please send signal like this: > 197 | killall -INT gdb 198 | Then you can get control in vgdb. 199 | 200 | ============================================================================== 201 | 5. Preview variable (auto expand) *vgdb-preview* 202 | 203 | If a structure/class has many member variables and you are only interested in some 204 | of them, or you want to make the variables more readable, this feature is very 205 | useful. MSVC debugger provides a file named "autoexp.dat" for you to define your 206 | own auto expand rules. VGdb is partially compatible with this file. 207 | 208 | You can find an example program cpp1.cpp and the sample autoexp.dat. In the 209 | program, class "SBOString" has a "SBOStringData" pointer and "SBOStringData" 210 | contains the real readable wchar_t string. If you print a SBOString variable, 211 | gdb outputs the pointer value. Now you can define the preview rule in 212 | autoexp.dat: > 213 | 214 | SBOString= str=m_str>, len=m_len> 215 | 216 | Then when you print the variable via or "p var", it just show > 217 | str="hello", len=10 218 | as you defined. 219 | 220 | 221 | Another class MONEY use integer to store data, e.g. 1.1 is stored as 1100000. 222 | To make it more readable, you can define rule: > 223 | 224 | MONEY= 225 | 226 | The double value is show when you preview the variable. 227 | 228 | Note: 229 | "autoexp.dat" is in your $HOME path or the current path when you start 230 | debugging. 231 | 232 | Change in autoexp.dat will immediately take effects. 233 | 234 | ============================================================================== 235 | 6. VGdb client *vgdbc* 236 | 237 | VGdb is a client-server program via socket that enables it both runs on MS 238 | Windows and Linux. The perl program vgdb is the server program. It also works 239 | as a client by "-c" option, for example: > 240 | 241 | vgdb -c "b main" 242 | 243 | It is called by vim. 244 | The port is auto generated and stored in the environment variable *$VGDB_PORT* . 245 | You can directly set the variable to force use some port. 246 | 247 | On MS Windows, "vgdb -c" pops up a console window every time as it's a perl 248 | command-line program. Although all functions work, it does not perform perfect. 249 | So I write vgdbc.c and build out vgdbc.dll that is used by vim (via libcall) 250 | acting as the client program. With the dll, when you are debugging step by step, 251 | the performance is very good. Note the dll should be put into the search path. 252 | 253 | You can test if the DLL works in your vim by: > 254 | 255 | :libcall('vgdbc.dll', 'test', '') 256 | :libcall('vgdbc.dll', 'tcpcall', 'help') 257 | 258 | On Linux, it's built to be the libvgdbc.so and it is optional as the "vgdb -c" 259 | also performs well. Test on Linux: > 260 | 261 | :libcall('libvgdbc.so', 'test', '') 262 | 263 | If the binary does not work on your system, you can make it by yourself: > 264 | 265 | $ make 266 | 267 | vgdb auto judge if the lib works in vim. If true, variable *g:vgdb_uselibcall* 268 | is set. You can inspect this variable: > 269 | 270 | echo g:vgdb_uselibcall 271 | 272 | 273 | ============================================================================== 274 | 7. Contact me *vgdb-contact* 275 | 276 | Liang, Jian - skyshore@gmail.com 277 | 278 | Thanks to the following softwares that give my ideas: > 279 | 280 | gdbvim (http://www.vim.org/scripts/script.php?script_id=84) 281 | vimgdb (http://www.vim.org/scripts/script.php?script_id=3039) 282 | MSVC 283 | pyclewn 284 | 285 | vim:tw=78:ts=8:sw=8:ft=help:norl: 286 | -------------------------------------------------------------------------------- /autoexp.dat: -------------------------------------------------------------------------------- 1 | SBOString= str=m_str>, len=m_len> 2 | MONEY= 3 | _DBM_DataAccessGate= tbl=m_str>, size=, bcksize=, dirty= 4 | 5 | -------------------------------------------------------------------------------- /calctax.pl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyshore2001/vgdb-vim/64e10cd8add15b60a410239aec0b2950e0afaf0c/calctax.pl -------------------------------------------------------------------------------- /cpp1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MUL 1000000.0 7 | 8 | class MoneyOverflowException: public std::runtime_error 9 | { 10 | public: 11 | MoneyOverflowException(): std::runtime_error("Money Overflow") {} 12 | }; 13 | 14 | class MONEY 15 | { 16 | public: 17 | MONEY(double d) { 18 | m_data[0] = (int64_t)(d * MUL); 19 | m_data[1] = 0; 20 | } 21 | double ToDouble() const { 22 | return m_data[0] / MUL; 23 | } 24 | 25 | MONEY &Add(double d) 26 | { 27 | if (ToDouble() + d > 10000.0) 28 | { 29 | throw MoneyOverflowException(); 30 | } 31 | m_data[0] += (int64_t)(d * MUL); 32 | } 33 | 34 | private: 35 | int64_t m_data[2]; 36 | }; 37 | 38 | class SBOStringData 39 | { 40 | public: 41 | SBOStringData(const MONEY &m) { 42 | m_str = new wchar_t[50]; 43 | #ifdef _WINDOWS 44 | m_len = swprintf(m_str, L"%.2lf", m.ToDouble()); 45 | #else 46 | m_len = swprintf(m_str, 50, L"%.2lf", m.ToDouble()); 47 | #endif 48 | } 49 | ~SBOStringData() { 50 | delete m_str; 51 | } 52 | private: 53 | wchar_t *m_str; 54 | int m_len; 55 | friend class SBOString; 56 | }; 57 | 58 | class SBOString 59 | { 60 | public: 61 | SBOString(const MONEY &m) { 62 | m_strData = new SBOStringData(m); 63 | } 64 | ~SBOString() { 65 | delete m_strData; 66 | } 67 | const wchar_t *GetBuffer() const { 68 | return m_strData->m_str; 69 | } 70 | private: 71 | SBOStringData *m_strData; 72 | }; 73 | 74 | int main() 75 | { 76 | MONEY m(8888.123); 77 | try { 78 | for (int i=0; i<10; ++i) { 79 | m.Add(234.512); 80 | SBOString s(m); 81 | printf("i=%d, %lf, %ls\n", i, m.ToDouble(), s.GetBuffer()); 82 | } 83 | } 84 | catch (std::exception &e) { 85 | printf("Exception: %s\n", e.what()); 86 | } 87 | return 0; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /demo/__VGDB_DEMO__.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/__VGDB_DEMO__.js: -------------------------------------------------------------------------------- 1 | obj=document.getElementsByTagName('object'); 2 | for (var i=0; i 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/__VGDB_DEMO_v2.js: -------------------------------------------------------------------------------- 1 | obj=document.getElementsByTagName('object'); 2 | for (var i=0; i0}) 63 | { 64 | my $clsName = shift; 65 | my %opt = @_; 66 | my $this = bless { 67 | type => 'inet', 68 | isclient => $opt{isclient} 69 | }, $clsName; 70 | if ($this->{isclient}) { 71 | $this->{sock} = IO::Socket::INET->new ( 72 | PeerAddr => '127.0.0.1', 73 | PeerPort => $VGDB_PORT, 74 | Proto => 'tcp', 75 | ) or die "Cannot connect to vgdb. Please check if vgdb is listening on tcp:$VGDB_PORT! Failed"; 76 | } 77 | else { 78 | $this->{sock} = IO::Socket::INET->new ( 79 | #LocalAddr => '127.0.0.1', 80 | LocalPort => $VGDB_PORT, 81 | Reuse => 1, 82 | # ReuseAddr => 1, 83 | # ReusePort => 1, 84 | Proto => 'tcp', 85 | Listen => 1, 86 | ) or die "cannot open socket: $!"; 87 | } 88 | $this->{sock}->autoflush(1); 89 | $this; 90 | } 91 | 92 | sub destroy 93 | { 94 | my $this = shift; 95 | if ($this->{session}) { 96 | $this->{session}->close(); 97 | } 98 | $this->{sock}->close(); 99 | } 100 | 101 | sub put # ($line) 102 | { 103 | my $this = shift; 104 | my $line = $_[0]; 105 | my $sck = $this->{isclient}? $this->{sock}: $this->{session} ; 106 | print $sck $line; 107 | } 108 | 109 | sub get 110 | { 111 | my $this = shift; 112 | my $sck; 113 | local $_; 114 | if ($this->{isclient}) { 115 | $sck = $this->{sock}; 116 | } 117 | else { 118 | if ($this->{session}) { 119 | $this->{session}->close(); 120 | delete $this->{session}; 121 | } 122 | $sck = $this->{session} = $this->{sock}->accept(); 123 | } 124 | $_ = <$sck>; 125 | } 126 | 127 | # sub accept 128 | # { 129 | # my $this = shift; 130 | # if ($this->{session}) { 131 | # $this->{session}->close(); 132 | # delete $this->{session}; 133 | # } 134 | # $this->{session} = $this->{sock}->accept(); 135 | # $this->{session}->autoflush(1); 136 | # 1; 137 | # } 138 | #}}} 139 | 140 | package CommMem; # {{{ 141 | sub new # () 142 | { 143 | my $clsName = shift; 144 | my $this = bless { 145 | type => 'mem', 146 | val => '', 147 | }, $clsName; 148 | $this; 149 | } 150 | 151 | sub destroy 152 | { 153 | my $this = shift; 154 | } 155 | 156 | sub put # ($line) 157 | { 158 | my $this = shift; 159 | $this->{var} .= $_[0]; 160 | } 161 | 162 | sub get 163 | { 164 | my $this = shift; 165 | local $_ = $this->{var}; 166 | $this->{var} = ''; 167 | $_; 168 | } 169 | #}}} 170 | 171 | package main; 172 | 173 | ###### run as client {{{ 174 | my $isclient = ($ARGV[0] && $ARGV[0] eq '-c'); 175 | sub runClient # ($cmds) 176 | { 177 | my ($cmds) = @_; 178 | my $comm = CommInet->new(isclient=> 1); 179 | $comm->put("$cmds\n"); 180 | 181 | my $line; 182 | while(defined ($line = $comm->get())) 183 | { 184 | print $line; 185 | } 186 | $comm->destroy(); 187 | } 188 | 189 | if ($isclient) { 190 | runClient($ARGV[1]); 191 | exit; 192 | } 193 | 194 | #}}} 195 | 196 | ###### toolkit {{{ 197 | sub mychop 198 | { 199 | $_[0] =~ s/[ \r\n]+$//; 200 | } 201 | 202 | sub msg # ($msg, [$force=0]) 203 | { 204 | print STDERR $_[0] if $ENV{VGDB_DEBUG} || $_[1]; 205 | } 206 | #}}} 207 | 208 | ###### functions {{{ 209 | ### Globals 210 | my @cmdlist; 211 | my $DBG; 212 | 213 | my $is_debugging = 0; 214 | sub parseGdbLine # ($comm, $line, $hideout) 215 | { 216 | my $comm = $_[0]; 217 | my $cnt = 0; 218 | return $cnt unless defined $comm; 219 | 220 | local $_ = $_[1]; 221 | my $hideout = $_[2]; 222 | my $cmd; 223 | # set a BP: 224 | # 'b main' -> 'Breakpoint 2 at 0x4013cb: file cpp1.cpp, line 13.' 225 | # 'Breakpoint 2 (/home/builder/depot/BUSMB_B1/SBO/9.01_DEV/BuildBuilder/CreatorDll/TmScrParser.cpp:115) pending.' 226 | # encounter a BP: 227 | # 'Breakpoint 11, DBMCSqlStatement::ExecDirect (this=0x7fffffff9350, query=..., env=0x637760) at ../Infrastructure/Engines/DBM/__DBMC_Statement.cpp:178' 228 | # 'Breakpoint 17 at 0x7fc3f1f8b523: B1FileWriter.cpp:268. (2 locations)' 229 | if (/^Breakpoint (\d+) at [^:]+: file ([^,]+), line (\d+)/ || /^Breakpoint (\d+) \((..[^:]+):(\d+)\) (pending)./ || /^Breakpoint (\d+).* at (?:0x\S+ )?(..[^:]+):(\d+)/) { 230 | my ($id, $f, $ln, $hint) = ($1, $2, $3, $4 || ''); 231 | $f =~ s/\\/\//g; 232 | $cmd = "setbp($id, \"$f\", $ln, \"$hint\")"; 233 | } 234 | # 'clear cpp1.cpp:13' -> 'Deleted breakpoint 1' 235 | elsif (/^Deleted breakpoint (\d+)/) { 236 | $cmd = "delbp($1)"; 237 | } 238 | # auto "up" on catchpoint hits 239 | # Catchpoint 2 (exception thrown), 0x004014f0 in __cxa_throw () 240 | # Catchpoint 3 (exception caught), 0x00401518 in __cxa_begin_catch () 241 | elsif (/^Catchpoint (\d+) \(exception (thrown|caught)\)/) { 242 | unshift @cmdlist, "up"; 243 | msg "=== auto frame up\n"; 244 | } 245 | # '[Inferior 1 (process 11468) exited normally]' 246 | # 'Kill the program being debugged? (y or n) [answered Y; input not from terminal]' 247 | # 'The program is not being run.' 248 | elsif (/Inferior/ || /^Kill the program/ || /^Program exited/ || /^The program is not being run/ || /^The program no longer exists/ || /^Detaching from/) { 249 | $is_debugging = 0; 250 | $cmd = "delpos()"; 251 | } 252 | # 'Starting program: /mnt/data/depot2/BUSMB_B1/B1OD/20_DEV/c/9.01/sbo/Source/LinuxProjects/bin-x64/mytest' 253 | elsif (/^Starting program/ || /^Attaching to/) { 254 | $is_debugging = 1; 255 | $cmd = "setpos(\"\", 0)"; 256 | } 257 | # cd bin 258 | # Working directory c:\prog\myprog\bin. 259 | elsif (/^Working directory (.+)\.$/) { 260 | my $d = $1; 261 | $d =~ s/[ \\]/\\$&/g; 262 | $cmd = "exe(\"cd $d\")"; 263 | } 264 | # 'r' -> 'C:\Users\i058537\cpp1.cpp:13:191:beg:0x4013cb' 265 | elsif (/\032\032(..[^:]*):(\d+)/) { 266 | my ($f, $ln) = ($1, $2); 267 | $f =~ s/\\/\//g; 268 | $cmd = "setpos(\"$f\", $ln)"; 269 | $hideout =1; 270 | $is_debugging = 1; 271 | 272 | # bugfix: should be 2 lines but in 1 line, e.g. "hello\032\032..." 273 | if (! /^\032/) { 274 | s/\032.*$//; 275 | $hideout = 0; 276 | } 277 | } 278 | do { $comm->put("$_\n"); ++ $cnt } unless $hideout; 279 | if (defined $cmd) { 280 | $comm->put("vi:$cmd\n"); 281 | msg "=== 'vi:$cmd'\n"; 282 | } 283 | $cnt; 284 | } 285 | 286 | sub initDebug # ($comm) 287 | { 288 | if ($DBG eq 'gdb') { 289 | # execCmd(undef, 'set prompt (gdb)\n'); 290 | if ($IS_MSWIN) { 291 | execCmd(undef, 'set new-console on'); 292 | } 293 | execCmd(undef, 'set print pretty on'); 294 | execCmd(undef, 'set breakpoint pending on'); 295 | execCmd(undef, 'set pagination off'); 296 | } 297 | else { 298 | execCmd(undef, '$| = 1'); 299 | } 300 | } 301 | 302 | # for perldb 303 | my %BP; 304 | my $BP_N = 1; 305 | sub filterCmd # ($cmd, $comm) 306 | { 307 | my ($cmd, $comm) = @_; 308 | if ($DBG eq 'perldb') { # mapping to gdb cmd 309 | if ($cmd eq 'fin' || $cmd eq 'finish') { 310 | $cmd = 'r'; 311 | } 312 | elsif ($cmd eq 'bt' || $cmd eq 'where') { 313 | $cmd = 'T'; 314 | } 315 | elsif ($cmd eq 'next') { 316 | $cmd = 'n'; 317 | } 318 | elsif ($cmd eq 'step') { 319 | $cmd = 's'; 320 | } 321 | elsif ($cmd eq 'k' || $cmd eq 'kill') { 322 | $cmd = 'q'; 323 | } 324 | elsif ($cmd eq 'cont' || $cmd eq 'continue') { 325 | $cmd = 'c'; 326 | } 327 | elsif ($cmd eq 'i br' || $cmd eq 'info break') { 328 | $cmd = 'L'; 329 | } 330 | else { 331 | my ($id, $f, $ln, $hint); 332 | if ($cmd =~ /b(?:reak)? (.+?):(\d+)/) { 333 | # TODO: diff file / fail to set bp 334 | ($id, $f, $ln, $hint) = ($BP_N, $1, $2, ''); 335 | $BP{$BP_N} = $ln; 336 | ++ $BP_N; 337 | $cmd = "b $ln"; 338 | my $vicmd = "setbp($id, \"$f\", $ln, \"$hint\")"; 339 | $comm->put("vi:$vicmd\n"); 340 | msg "=== 'vi:$vicmd'\n"; 341 | } 342 | elsif ($cmd =~ /d(?:elete)? (\d+)/) { 343 | # TODO: diff file / fail to set bp 344 | $id = $1; 345 | if (exists($BP{$id})) { 346 | $ln = $BP{$id}; 347 | $cmd = "B $ln"; 348 | undef $BP{$id}; 349 | } 350 | # TODO: re-set db when init 351 | } 352 | } 353 | } 354 | $cmd; 355 | } 356 | 357 | sub matchPrompt # ($line, $ch) 358 | { 359 | if ($DBG eq 'perldb') 360 | { 361 | return if $_[1] ne ' '; 362 | # prompt: " DB<1> " 363 | return if length($_[0]) < 8; 364 | return if substr($_[0], -2, 1) ne '>'; 365 | return $_[0] =~ /^ DB<+\d+>+ $/; 366 | } 367 | elsif ($DBG eq 'gdb') 368 | { 369 | return if $_[1] ne ' '; 370 | # prompt: " DB<1> " 371 | return $_[0] eq '(gdb) '; 372 | } 373 | } 374 | 375 | # read until "\n" or 376 | sub mygetline # ($in, \$isEnd) 377 | { 378 | my ($in, $pIsEnd) = @_; 379 | 380 | my ($line, $ch, $ch2); 381 | while (1) { 382 | read ($in, $ch, 1) or last; 383 | # msg "###ch###n"; 384 | $line .= $ch; 385 | last if $ch eq "\n"; 386 | do {$$pIsEnd =1; last} if matchPrompt($line, $ch); 387 | } 388 | $line; 389 | } 390 | 391 | # exec gdb command, write result to $comm 392 | # return undef - quit command 393 | # *** ignore the prompt-only response. 394 | my $lastPrompt = ''; 395 | sub execCmd # ($comm, $cmd, [$hideout=0], [$noPrompt=0]) 396 | { 397 | my ($comm, $cmd, $hideout, $noPrompt) = @_; 398 | if (defined $cmd) { 399 | $cmd = filterCmd($cmd, $comm); 400 | print GDB_WR "$cmd\n"; 401 | msg "(gdb) '$cmd'\n"; 402 | } 403 | my $isEnd = 0; 404 | my $cnt = 0; 405 | while (defined ($_ = mygetline(\*GDB_RD, \$isEnd))) { 406 | s/[\r\n]+$//; 407 | msg ">>> '$_'\n", !/^\032/; 408 | if ($isEnd) { 409 | if ($comm && !$hideout && !$noPrompt && ($cnt > 0 || $_ ne $lastPrompt)) 410 | { 411 | $comm->put($_); 412 | $lastPrompt = $_; 413 | } 414 | # post action 415 | if (defined $cmd && $DBG eq 'perldb') { 416 | # restart 417 | if ($cmd eq 'R') { 418 | initDebug($comm); 419 | } 420 | } 421 | return 1; 422 | } 423 | $cnt += parseGdbLine($comm, $_, $hideout); 424 | } 425 | 426 | # gdb quits 427 | $comm->put("vi:quit()\n"); 428 | msg "=== 'vi:quit'\n"; 429 | return; 430 | } 431 | 432 | # feature: 1. auto tbreak; 2. allow jump cross-function (MUST set frame to the target function before) 433 | sub execJumpCmd # ($comm, $cmd, [$hideout=0]) 434 | { 435 | my ($comm, $cmd, $hideout) = @_; 436 | my @a = split(/\s+/, $cmd, 2); 437 | my $arg = $a[1]; 438 | 439 | # backup and restore stack if not at top frame (often user switch to other frame and jump in that frame) 440 | my $commMem = CommMem->new(); 441 | local $_ = getCmdResult($commMem, 'frame'); 442 | my $restoreStack = !/^#0\s/; 443 | my $isX86; 444 | 445 | if ($restoreStack) { 446 | $_ = getCmdResult($commMem, 'whatis $rbp'); 447 | $isX86 = ($_ =~ /type = void$/); # on x64, it shows "type = void *" 448 | 449 | # backup stack 450 | if ($isX86) { 451 | execCmd(undef, 'set $ebp1=$ebp'); 452 | execCmd(undef, 'set $esp1=$esp'); 453 | } 454 | else { # x64 455 | execCmd(undef, 'set $rbp1=$rbp'); 456 | execCmd(undef, 'set $rsp1=$rsp'); 457 | } 458 | } 459 | 460 | execCmd(undef, 'tbreak ' . $arg); 461 | execCmd($comm, 'jump ' . $arg, $hideout); 462 | 463 | if ($restoreStack) { 464 | # e.g. "Line 78 is not in `AddMoney(MONEY*, double)'. Jump anyway? (y or n)" 465 | # restore stack 466 | if ($isX86) { 467 | execCmd(undef, 'set $ebp=$ebp1'); 468 | execCmd(undef, 'set $esp=$esp1'); 469 | } 470 | else { # x64 471 | execCmd(undef, 'set $rbp=$rbp1'); 472 | execCmd(undef, 'set $rsp=$rsp1'); 473 | } 474 | } 475 | } 476 | 477 | sub execVgdbCmd # ($comm, $cmd, [$hideout=0]) 478 | { 479 | my ($comm, $cmd, $hideout) = @_; 480 | my $rv = 1; 481 | msg "(vgdb) '$cmd'\n"; 482 | if ($cmd eq 'c') { 483 | $rv = execCmd($comm, $is_debugging? 'continue': 'run', $hideout); 484 | } 485 | elsif ($cmd eq 'ver' || $cmd eq 'version') { 486 | $comm->put($VERINFO); 487 | } 488 | elsif ($cmd =~ /^ju(mp)?\s/) { 489 | $rv = execJumpCmd($comm, $cmd, $hideout); 490 | } 491 | elsif ($cmd =~ /^debug=(.+)$/) { 492 | $ENV{VGDB_DEBUG} = $1; 493 | } 494 | elsif ($cmd =~ /^p\s+(.+)$/) { 495 | $rv = execPreviewCmd($comm, $1, $hideout); 496 | } 497 | elsif ($cmd eq 'init') { 498 | $comm->put("vi:setdbg(\"$DBG\")\n"); 499 | $rv = execCmd($comm, undef); # no cmd, just get e.g. the init msg 500 | initDebug($comm); 501 | } 502 | else { 503 | $comm->put("!!! unknown vgdb command: '$cmd'\n"); 504 | msg "!!! unknown vgdb command: '$cmd'\n", 1; 505 | } 506 | return $rv; 507 | } 508 | 509 | =example: 510 | 1. define in autoexp.dat: 511 | SBOString= m_str>, len=m_len> 512 | 513 | 2. demo session with gdb: 514 | (gdb) whatis s 515 | type = SBOString 516 | (gdb) p &(s) 517 | $32 = (SBOString *) 0x28ff0c 518 | (gdb) p $32->m_strData->m_str 519 | $33 = 0x661948 L"3.141500" 520 | (gdb) p $32->m_strData->m_len 521 | $34 = 8 522 | 523 | 3. the final show of vgdb: 524 | L"3.141500", len=8 525 | 526 | =cut 527 | 528 | # throw 'quit' if gdb quits; otherwize return the cmd result 529 | sub getCmdResult # ($commMem, $cmd) 530 | { 531 | my $rv = execCmd($_[0], $_[1], undef, 'noPrompt'); 532 | die 'quit' unless defined $rv; # encounter 'quit' 533 | $_[0]->get(); 534 | } 535 | 536 | my $last_mtime = 0; 537 | my %autoexp; 538 | sub loadAutoexp 539 | { 540 | my $f = "autoexp.dat"; 541 | unless (-r $f) { 542 | my $path; 543 | if ($IS_MSWIN) { 544 | $path = $ENV{HOMEDRIVE} . $ENV{HOMEPATH}; 545 | } 546 | else { 547 | $path = $ENV{HOME}; 548 | } 549 | $f = "$path/$f"; 550 | } 551 | return unless -r $f; 552 | my @st = stat($f); 553 | my $mtime = $st[9]; 554 | return if ($mtime <= $last_mtime); 555 | $last_mtime = $mtime; 556 | 557 | %autoexp = (); 558 | open F, "$f"; 559 | while () { 560 | if (/(\S+)\s*=\s*(.*?)\s*$/) { 561 | $autoexp{$1} = $2; 562 | } 563 | } 564 | close F; 565 | } 566 | 567 | # return undef - quit command; 1 - Ok 568 | sub execPreviewCmd # ($comm, $expr, $hideout) 569 | { 570 | my ($comm, $expr, $hideout) = @_; 571 | if ($DBG ne 'gdb') { 572 | return execCmd($comm, "p $expr", $hideout); 573 | } 574 | 575 | my $commMem = CommMem->new(); 576 | my ($type, $isptr); 577 | loadAutoexp(); 578 | local $_; 579 | eval { 580 | # 1. get type 581 | $_ = getCmdResult($commMem, "whatis $expr"); 582 | return 1 if !$_; 583 | mychop($_); 584 | unless(/^type = (.*)$/m) { 585 | $comm->put($_); 586 | return 1; 587 | } 588 | $type = $1; 589 | $comm->put("($type) "); 590 | my $mainType; 591 | while ($type =~ /\w+/g) { 592 | if ($& ne 'const') { 593 | unless (defined $mainType) { 594 | $mainType = $&; 595 | } 596 | else 597 | { 598 | $mainType = $type; 599 | last; 600 | } 601 | } 602 | } 603 | $isptr =1 if $type =~ /\*/; 604 | unless (exists $autoexp{$mainType}) { 605 | return execCmd($comm, "p $expr", $hideout); 606 | } 607 | 608 | # 2. get pointer 609 | unless ($isptr) { 610 | $_ = getCmdResult($commMem, "p &($expr)"); 611 | if (! /(\$\d+) = /) { 612 | return execCmd($comm, "p $expr", $hideout); 613 | } 614 | $expr = $1; 615 | } 616 | # 3. eval 617 | # e.g. "m_str, su>, m_len>"; 618 | my $expand = sub { # ($expandExpr) 619 | my $expandExpr = shift; 620 | my $showType = ''; 621 | # get and remove $showType from expandExpr: "m_strData->m_str, su" -> "m_strData->m_str" 622 | $expandExpr =~ s/\s*,\s*(\S+)\s*$/$showType=$1; ''/e; 623 | # set expandExpr="($expr)->m_strData->m_str" 624 | $expandExpr =~ s/(?])\b(?=[a-zA-Z_])/($expr)->/g; 625 | local $_ = getCmdResult($commMem, "p $expandExpr"); 626 | s/^.+? = (0x\S+ )?//; 627 | s/\n//; 628 | $_; 629 | }; 630 | # e.g. "m_strData->m_str" -> L"Hello" 631 | $_ = $autoexp{$mainType}; 632 | s/<(.*?(?/&$expand($1)/eg; 633 | $comm->put($_); 634 | }; 635 | if ($@ =~ /^quit /) { 636 | $comm->put($commMem->get()); 637 | return undef; 638 | } 639 | 1; 640 | } 641 | 642 | # require: @ARGV 643 | sub guessDBG 644 | { 645 | local $_ = $ARGV[0]; 646 | return 'gdb' if !defined $_; 647 | if ($_ eq '-pl') { 648 | shift @ARGV; 649 | return 'perldb'; 650 | } 651 | if ($_ eq '-py') { 652 | shift @ARGV; 653 | return 'pydb'; 654 | } 655 | if ($_ eq '-js') { 656 | shift @ARGV; 657 | return 'jsdb'; 658 | } 659 | 660 | if (/\.pl$/i) { 661 | return 'perldb'; 662 | } 663 | if (/\.py$/i) { 664 | return 'pydb'; 665 | } 666 | if (/\.js$/i) { 667 | return 'jsdb'; 668 | } 669 | 670 | # check "#!perl" 671 | if (-f $_) { 672 | my $line; 673 | open I, $_; 674 | read I, $line, 100; 675 | $line =~ s/[\r\n].*$//s; 676 | close I; 677 | if ($line =~ /^#!/) { 678 | if ($line =~ /perl/i) { 679 | return 'perldb'; 680 | } 681 | if ($line =~ /python/i) { 682 | return 'pydb'; 683 | } 684 | } 685 | } 686 | 'gdb' 687 | } 688 | 689 | # handle @ARGV 690 | sub startGdb 691 | { 692 | $DBG = guessDBG(); 693 | 694 | my @cmd; 695 | if ($DBG eq 'gdb') { 696 | # -q: no version info; -f: output for emacs (file:lineno after each cmd) 697 | @cmd = ('gdb', '-f', '-q', @ARGV); 698 | #my $gdbcmd = "gdb --interpreter=mi "; 699 | if (!$IS_MSWIN) { 700 | if ($TTY && grep /tty/, @ARGV) { 701 | push @cmd, "--tty=$TTY"; 702 | } 703 | } 704 | } 705 | elsif ($DBG eq 'perldb') { 706 | # !!! set $TERM to disable the colored show 707 | $ENV{TERM} = 'dumb'; 708 | if (!$IS_MSWIN) { 709 | # !!! disable create new tty. according to source code of perl5db.pl: 710 | # try to set $console = undef to disable new tty 711 | $ENV{OS2_SHELL} = 'dumb'; 712 | } 713 | my $script = shift @ARGV; 714 | @cmd = ('perl', '-d', $script, '-emacs', @ARGV); 715 | } 716 | else { 717 | die "NotImplemented: $DBG"; 718 | } 719 | 720 | use IPC::Open3; 721 | msg "start $DBG: '" . join(' ', @cmd) . "'\n"; 722 | my $gdb_task = open3(\*GDB_WR, \*GDB_RD, \*GDB_RD, @cmd) 723 | or die("start $DBG error: $!\n"); 724 | my $p = select(GDB_WR); $| = 1; select($p); # make unbuffered 725 | } 726 | 727 | sub startVim 728 | { 729 | # use threads; 730 | my $VIM_EXE = 'gvim'; 731 | msg "starting VIM.\n"; 732 | # threads->new(sub { 733 | my $cmd = $VIM_EXE . ' -c "call VGdb_open()"'; 734 | if ($IS_MSWIN) { 735 | $cmd = "start $cmd"; 736 | } 737 | else { 738 | $cmd .= " &"; 739 | } 740 | system($cmd); 741 | # })->detach(); 742 | } 743 | #}}} 744 | 745 | ###### main routine {{{ 746 | my $calledByVim = $ARGV[0] && $ARGV[0] eq '-vi'; 747 | if ($calledByVim) { 748 | shift @ARGV; 749 | } 750 | 751 | msg $VERINFO, 1; 752 | 753 | startGdb(); 754 | 755 | my $comm = CommInet->new(); 756 | msg "vgdb is listening on TCP $VGDB_PORT\n"; 757 | 758 | unless ($calledByVim) 759 | { 760 | # note: after TCP port is listening 761 | startVim(); 762 | } 763 | 764 | my $gdbQuit = 0; 765 | while(! $gdbQuit) 766 | { 767 | my $cmds = $comm->get() || ''; 768 | mychop($cmds); 769 | @cmdlist = (); 770 | if ($cmds eq '' || index($cmds, '"') >= 0) { 771 | push @cmdlist, $cmds; 772 | } 773 | else { 774 | @cmdlist = split /;/, $cmds; 775 | } 776 | while (defined (local $_ = shift @cmdlist)) { 777 | s/^\s+//; 778 | s/\s+$//; 779 | 780 | my $hideout = s/^@//; 781 | my $rv; 782 | if (/^\.(.+)$/) { # begin with "." 783 | $rv = execVgdbCmd($comm, $1, $hideout); 784 | } 785 | else { 786 | $rv = execCmd($comm, $_, $hideout); 787 | } 788 | unless (defined $rv) { 789 | $gdbQuit = 1; 790 | last; 791 | } 792 | } 793 | } 794 | $comm->destroy(); 795 | msg "vgdb exits\n"; 796 | #}}} 797 | 798 | # vim: set foldmethod=marker : 799 | -------------------------------------------------------------------------------- /vgdb.bat: -------------------------------------------------------------------------------- 1 | @perl %CD%\vgdb %* 2 | -------------------------------------------------------------------------------- /vgdb.vim: -------------------------------------------------------------------------------- 1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""" 2 | " vgdb - Vim plugin for interface to gdb from cterm 3 | " Maintainer: Liang, Jian (skyshore@gmail.com) 4 | " Thanks to gdbvim and vimgdb. 5 | " 6 | " Feedback welcome. 7 | """"""""""""""""""""""""""""""""""""""""""""""""""""""" 8 | 9 | " Prevent multiple loading unless: let force_load=1 10 | if exists("loaded_vgdb") && !exists("force_load") 11 | finish 12 | endif 13 | 14 | let s:ismswin=has('win32') 15 | 16 | " ====== config {{{ 17 | let loaded_vgdb = 1 18 | let s:vgdb_winheight = 10 19 | let s:vgdb_bufname = "__VGDB__" 20 | let s:vgdb_prompt = '(gdb) ' 21 | let s:dbg = 'gdb' 22 | 23 | let s:perldbPromptRE = '^\s*DB<\+\d\+>\+\s*' 24 | 25 | " used by system-call style 26 | let s:vgdb_client = "vgdb -c" " on MSWin, actually the vgdb.bat is called in the search path 27 | " used by libcall style 28 | let s:vgdb_lib = s:ismswin ? 'vgdbc.dll' : 'libvgdbc.so' 29 | "}}} 30 | " ====== global {{{ 31 | let s:bplist = {} " id => {file, line, disabled} that returned by gdb 32 | 33 | let s:vgdb_running = 0 34 | let s:debugging = 0 35 | " for pathfix 36 | let s:unresolved_bplist = {} " elem: file_basename => %bplist 37 | "let s:pathMap = {} " unresolved_path => fullpath 38 | let s:nameMap = {} " file_basename => {fullname, pathFixed=0|1}, set by s:getFixedPath() 39 | 40 | "let g:vgdb_perl = 0 41 | let g:vgdb_uselibcall=has('libcall') 42 | if g:vgdb_uselibcall 43 | try 44 | let s = libcall(s:vgdb_lib, 'test', 'libcall test') 45 | catch 46 | let g:vgdb_uselibcall = 0 47 | endtry 48 | endif 49 | 50 | let s:set_disabled_bp = 0 51 | "}}} 52 | " ====== syntax {{{ 53 | highlight DebugBreak guibg=darkred guifg=white ctermbg=darkred ctermfg=white 54 | highlight DisabledBreak guibg=lightred guifg=black ctermbg=lightred ctermfg=black 55 | sign define vgdbBreakpoint linehl=DebugBreak text=B> 56 | sign define vgdbDisabledbp linehl=DisabledBreak text=b> 57 | " sign define current linehl=DebugStop 58 | sign define vgdbCurrent linehl=Search text=>> texthl=Search 59 | 60 | " highlight vgdbGoto guifg=Blue 61 | hi def link vgdbKey Statement 62 | hi def link vgdbHiLn Statement 63 | hi def link vgdbGoto Underlined 64 | hi def link vgdbPtr Underlined 65 | hi def link vgdbFrame LineNr 66 | hi def link vgdbCmd Macro 67 | "}}} 68 | " ====== toolkit {{{ 69 | let s:match = [] 70 | function! s:mymatch(expr, pat) 71 | let s:match = matchlist(a:expr, a:pat) 72 | return len(s:match) >0 73 | endf 74 | 75 | function! s:dirname(file) 76 | if s:ismswin 77 | let pos = strridx(a:file, '\') 78 | else 79 | let pos = strridx(a:file, '/') 80 | endif 81 | return strpart(a:file, 0, pos) 82 | endf 83 | 84 | function! s:basename(file) 85 | " let f = substitute(file, '\', '/', 'g') 86 | let pos = strridx(a:file, '/') 87 | if pos<0 && s:ismswin 88 | let pos = strridx(a:file, '\') 89 | endif 90 | return strpart(a:file, pos+1) 91 | endf 92 | "}}} 93 | " ====== app toolkit {{{ 94 | function! s:gotoGdbWin() 95 | if bufname("%") == s:vgdb_bufname 96 | return 97 | endif 98 | let gdbwin = bufwinnr(s:vgdb_bufname) 99 | if gdbwin == -1 100 | " if multi-tab or the buffer is hidden 101 | call s:VGdb_openWindow() 102 | let gdbwin = bufwinnr(s:vgdb_bufname) 103 | endif 104 | exec gdbwin . "wincmd w" 105 | endf 106 | 107 | function! s:gotoTgtWin() 108 | let gdbwin = bufwinnr(s:vgdb_bufname) 109 | if winnr() == gdbwin 110 | exec "wincmd p" 111 | endif 112 | endf 113 | 114 | function! s:VGdb_bpkey(file, line) 115 | return a:file . ":" . a:line 116 | endf 117 | 118 | function! s:VGdb_curpos() 119 | " ???? filename ???? 120 | let file = expand("%:t") 121 | let line = line(".") 122 | return s:VGdb_bpkey(file, line) 123 | endf 124 | 125 | function! s:placebp(id, line, bnr, disabled) 126 | let name = (!a:disabled)? "vgdbBreakpoint": "vgdbDisabledbp" 127 | execute "sign place " . a:id . " name=" . name . " line=" . a:line. " buffer=" . a:bnr 128 | endf 129 | 130 | function! s:unplacebp(id) 131 | execute "sign unplace ". a:id 132 | endf 133 | 134 | function! s:setbp(file, lineno, disabled) 135 | if a:file == "" || a:lineno == 0 136 | let key = s:VGdb_curpos() 137 | else 138 | let key = s:VGdb_bpkey(a:file, a:lineno) 139 | endif 140 | let s:set_disabled_bp = a:disabled 141 | call VGdb("break ".key) 142 | let s:set_disabled_bp = 0 143 | " will auto call back s:VGdb_cb_setbp 144 | endf 145 | 146 | function! s:delbp(id) 147 | call VGdb("delete ".a:id) 148 | call s:VGdb_cb_delbp(a:id) 149 | " call VGdb("clear ".key) 150 | endf 151 | 152 | "}}} 153 | 154 | " ====== functions {{{ 155 | " Get ready for communication 156 | function! s:VGdb_openWindow() 157 | let bufnum = bufnr(s:vgdb_bufname) 158 | 159 | if bufnum == -1 160 | " Create a new buffer 161 | let wcmd = s:vgdb_bufname 162 | else 163 | " Edit the existing buffer 164 | let wcmd = '+buffer' . bufnum 165 | endif 166 | 167 | " Create the tag explorer window 168 | exe 'silent! botright ' . s:vgdb_winheight . 'split ' . wcmd 169 | endfunction 170 | 171 | " NOTE: this function will be called by vgdb script. 172 | function! VGdb_open() 173 | " save current setting and restore when vgdb quits via 'so .exrc' 174 | mk! 175 | set nocursorline 176 | set nocursorcolumn 177 | 178 | call s:VGdb_openWindow() 179 | 180 | " Mark the buffer as a scratch buffer 181 | setlocal buftype=nofile 182 | setlocal bufhidden=delete 183 | setlocal noswapfile 184 | setlocal nowrap 185 | setlocal nobuflisted 186 | setlocal nonumber 187 | setlocal winfixheight 188 | setlocal cursorline 189 | 190 | setlocal foldcolumn=2 191 | setlocal foldtext=VGdb_foldTextExpr() 192 | setlocal foldmarker={,} 193 | setlocal foldmethod=marker 194 | 195 | augroup VGdbAutoCommand 196 | " autocmd WinEnter if line(".")==line("$") | starti | endif 197 | autocmd WinLeave stopi 198 | autocmd BufUnload call s:VGdb_bufunload() 199 | augroup end 200 | 201 | call s:VGdb_shortcuts() 202 | 203 | let s:vgdb_running = 1 204 | 205 | call VGdb(".init") " get init msg 206 | starti! 207 | 208 | "wincmd p 209 | endfunction 210 | 211 | function! s:VGdb_bufunload() 212 | if s:vgdb_running 213 | call VGdb('q') 214 | else 215 | call s:VGdb_cb_close() 216 | endif 217 | endfunction 218 | 219 | function! s:VGdb_goto(file, line) 220 | let f = s:getFixedPath(a:file) 221 | if strlen(f) == 0 222 | return 223 | endif 224 | call s:gotoTgtWin() 225 | if bufnr(f) != bufnr("%") 226 | if &modified || bufname("%") == s:vgdb_bufname 227 | execute 'new '.f 228 | else 229 | execute 'e '.f 230 | endif 231 | 232 | " resolve bp when entering new buffer 233 | let base = s:basename(a:file) 234 | if has_key(s:unresolved_bplist, base) 235 | let bplist = s:unresolved_bplist[base] 236 | for [id, bp] in items(bplist) 237 | call s:VGdb_cb_setbp(id, bp.file, bp.line) 238 | endfor 239 | unlet s:unresolved_bplist[base] 240 | endif 241 | endif 242 | 243 | execute a:line 244 | if has('folding') 245 | silent! foldopen! 246 | endif 247 | redraw 248 | " call winline() 249 | endf 250 | 251 | function! s:getFixedPath(file) 252 | if ! filereadable(a:file) 253 | let base = s:basename(a:file) 254 | if has_key(s:nameMap, base) 255 | return s:nameMap[base] 256 | endif 257 | if base == expand("%:t") 258 | let s:nameMap[base] = expand("%:p") 259 | return s:nameMap[base] 260 | endif 261 | let nr = bufnr(base) 262 | if nr != -1 263 | let s:nameMap[base] = bufname(nr) 264 | return s:nameMap[base] 265 | endif 266 | return "" 267 | endif 268 | return a:file 269 | endf 270 | 271 | " breakpoint highlight line may move after you edit the file. 272 | " this function re-set such BPs (except disables ones) that don't match the actual line. 273 | function! s:refreshBP() 274 | " get sign list 275 | lan message C 276 | redir => a 277 | sign place 278 | redir end 279 | 280 | " e.g. 281 | " Signs for cpp1.cpp: 282 | " line=71 id=1 name=disabledbp 283 | " line=73 id=1 name=disabledbp 284 | " Signs for /usr/share/vim/current/doc/eval.txt: 285 | " line=4 id=1 name=bp1 286 | 287 | let confirmed = 0 288 | for line in split(a, "\n") 289 | if s:mymatch(line, '\v\s+line\=(\d+)\s+id\=(\d+)') && has_key(s:bplist, s:match[2]) 290 | let lineno = s:match[1] 291 | let id = s:match[2] 292 | let bp = s:bplist[id] 293 | if bp.line != lineno 294 | if !confirmed 295 | let choice = confirm("Breakpoint position changes. Refresh now? (Choose No if the source code does not match the executable.)", "&Yes\n&No") 296 | if choice != 1 297 | break 298 | endif 299 | let confirmed = 1 300 | endif 301 | call s:delbp(id) 302 | call s:setbp(bp.file, lineno, bp.disabled) 303 | endif 304 | endif 305 | endfor 306 | endf 307 | 308 | function! s:setDebugging(val) 309 | if s:debugging != a:val 310 | if s:debugging == 0 " 0 -> 1: run/attach 311 | call s:refreshBP() 312 | endif 313 | let s:debugging = a:val 314 | endif 315 | endf 316 | 317 | "====== callback {{{ 318 | let s:callmap={ 319 | \'setdbg': 's:setdbg', 320 | \'setbp': 's:VGdb_cb_setbp', 321 | \'delbp': 's:VGdb_cb_delbp', 322 | \'setpos': 's:VGdb_cb_setpos', 323 | \'delpos': 's:VGdb_cb_delpos', 324 | \'exe': 's:VGdb_cb_exe', 325 | \'quit': 's:VGdb_cb_close' 326 | \ } 327 | 328 | function! s:setdbg(dbg) 329 | let s:dbg = a:dbg 330 | if s:dbg == "perldb" 331 | let s:vgdb_prompt = " DB<1> " 332 | elseif s:dbg == 'gdb' 333 | let s:vgdb_prompt = "(gdb) " 334 | endif 335 | endf 336 | 337 | function! s:VGdb_cb_setbp(id, file, line, ...) 338 | if has_key(s:bplist, a:id) 339 | return 340 | endif 341 | let hint = a:0>0 ? a:1 : '' 342 | let bp = {'file': a:file, 'line': a:line, 'disabled': s:set_disabled_bp} 343 | let f = s:getFixedPath(a:file) 344 | if (hint == 'pending' && bufnr(a:file) == -1) || strlen(f)==0 345 | let base = s:basename(a:file) 346 | if !has_key(s:unresolved_bplist, base) 347 | let s:unresolved_bplist[base] = {} 348 | endif 349 | let bplist = s:unresolved_bplist[base] 350 | let bplist[a:id] = bp 351 | return 352 | endif 353 | call s:VGdb_goto(f, a:line) 354 | " execute "sign unplace ". a:id 355 | if bp.disabled 356 | call VGdb("disable ".a:id) 357 | endif 358 | call s:placebp(a:id, a:line, bufnr(f), bp.disabled) 359 | let s:bplist[a:id] = bp 360 | endfunction 361 | 362 | function! s:VGdb_cb_delbp(id) 363 | if has_key(s:bplist, a:id) 364 | unlet s:bplist[a:id] 365 | call s:unplacebp(a:id) 366 | endif 367 | endf 368 | 369 | let s:last_id = 0 370 | function! s:VGdb_cb_setpos(file, line) 371 | if a:line <= 0 372 | call s:setDebugging(1) 373 | return 374 | endif 375 | 376 | let s:nameMap[s:basename(a:file)] = a:file 377 | call s:VGdb_goto(a:file, a:line) 378 | call s:setDebugging(1) 379 | 380 | " place the next line before unplacing the previous 381 | " otherwise display will jump 382 | let newid = (s:last_id+1) % 2 383 | execute "sign place " . (10000+newid) ." name=vgdbCurrent line=".a:line." buffer=".bufnr(a:file) 384 | execute "sign unplace ". (10000+s:last_id) 385 | let s:last_id = newid 386 | endf 387 | 388 | function! s:VGdb_cb_delpos() 389 | execute "sign unplace ". (10000+s:last_id) 390 | call s:setDebugging(0) 391 | endf 392 | 393 | function! s:VGdb_cb_exe(cmd) 394 | exe a:cmd 395 | endf 396 | 397 | function! s:VGdb_cb_close() 398 | if !s:vgdb_running 399 | return 400 | endif 401 | 402 | let s:vgdb_running = 0 403 | let s:bplist = {} 404 | let s:unresolved_bplist = {} 405 | sign unplace * 406 | if has('balloon_eval') 407 | set bexpr& 408 | endif 409 | 410 | " If gdb window is open then close it. 411 | call s:gotoGdbWin() 412 | quit 413 | 414 | silent! autocmd! VGdbAutoCommand 415 | if s:ismswin 416 | so _exrc 417 | else 418 | so .exrc 419 | endif 420 | endf 421 | 422 | "}}} 423 | 424 | function! VGdb_call(cmd) 425 | let usercmd = a:cmd 426 | if exists("g:vgdb_useperl") && g:vgdb_useperl 427 | perl <tmp1"; 429 | select O; 430 | my $usercmd = VIM::Eval("usercmd"); 431 | { 432 | local @ARGV = ('-c', $usercmd); 433 | do "d:/prog2/vgdb/vgdb"; 434 | } 435 | close O; 436 | EOF 437 | let lines = readfile("tmp1") 438 | elseif exists("g:vgdb_uselibcall") && g:vgdb_uselibcall 439 | let lines = libcall(s:vgdb_lib, "tcpcall", usercmd) 440 | else 441 | let usercmd = substitute(usercmd, '["$]', '\\\0', 'g') 442 | let lines = system(s:vgdb_client . " \"" . usercmd . "\"") 443 | endif 444 | return lines 445 | endf 446 | 447 | " mode: i|n|c| 448 | " i - input command in VGDB window and press enter 449 | " n - press enter (or double click) in VGDB window 450 | " c - run Gdb command 451 | function! VGdb(cmd, ...) " [mode] 452 | let usercmd = a:cmd 453 | let mode = a:0>0 ? a:1 : '' 454 | 455 | if s:vgdb_running == 0 456 | " let is_loadfile = 0 457 | " let is_loadfile = 1 458 | if !exists('$VGDB_PORT') 459 | if s:ismswin && g:vgdb_uselibcall 460 | " !!!! windows gvim has bug on libcall() - libcall cannot access 461 | " envvar defined in VIM. So here I make the port the same as 462 | " the one in the file vgdbc.c 463 | " LIMITATION: 1 debugging at one time on MSWin with libcall 464 | let $VGDB_PORT = 30899 465 | else 466 | let $VGDB_PORT= 30000 + reltime()[1] % 10000 467 | endif 468 | endif 469 | if s:ismswin 470 | " !!! "!start" is different from "! start" 471 | let startcmd = "!start vgdb.bat -vi " . usercmd 472 | else 473 | if !has('gui') 474 | let startcmd = "!vgdb -vi ".usercmd." &>/dev/null &" 475 | else 476 | let startcmd = "!vgdb -vi ".usercmd." &" 477 | endif 478 | endif 479 | exe 'silent '.startcmd 480 | call VGdb_open() 481 | " if is_loadfile 482 | " sleep 200 m 483 | " call VGdb("@tb main; r") 484 | " return 485 | " endif 486 | return 487 | endif 488 | 489 | if s:vgdb_running == 0 490 | echo "vgdb is not running" 491 | return 492 | endif 493 | 494 | let curwin = winnr() 495 | let stayInTgtWin = 0 496 | if s:dbg == 'gdb' && usercmd =~ '^\s*(gdb)' 497 | let usercmd = substitute(usercmd, '^\s*(gdb)\s*', '', '') 498 | elseif s:dbg == 'perldb' && usercmd =~ '^\s*DB<' 499 | let usercmd = substitute(usercmd, s:perldbPromptRE, '', '') 500 | elseif mode == 'i' 501 | " trim left and clean the search word 502 | s/^\s\+//e 503 | let @/='' 504 | if line('.') != line('$') 505 | call append('$', s:vgdb_prompt . usercmd) 506 | $ 507 | else 508 | exe "normal I" . s:vgdb_prompt 509 | endif 510 | endif 511 | " goto frame 512 | " i br (info breakpoints) 513 | " #0 0x00007fc54f6955e7 in recv () from /lib64/libpthread.so.0 514 | if s:mymatch(usercmd, '\v^#(\d+)') && s:debugging 515 | let usercmd = "@frame " . s:match[1] 516 | let stayInTgtWin = 1 517 | let mode = 'n' 518 | 519 | " goto thread and show frames 520 | " i thr (info threads) 521 | " 7 Thread 0x7fc54032b700 (LWP 25787) "java" 0x00007fc54f6955e7 in recv () from /lib64/libpthread.so.0 522 | elseif s:mymatch(usercmd, '\v^\s+(\d+)\s+Thread ') && s:debugging 523 | let usercmd = "@thread " . s:match[1] . "; bt" 524 | 525 | " Breakpoint 1, TmScrParser::Parse (this=0x7fffffffbbb0) at ../../BuildBuilder/CreatorDll/TmScrParser.cpp:64 526 | " Breakpoint 14 at 0x7ffff7bbeec1: file ../../BuildBuilder/CreatorDll/RDLL_SboP.cpp, line 111. 527 | " Breakpoint 6 (/home/builder/depot/BUSMB_B1/SBO/9.01_DEV/BuildBuilder/CreatorDll/RDLL_SboP.cpp:92) pending. 528 | " Breakpoint 17 at 0x7fc3f1f8b523: B1FileWriter.cpp:268. (2 locations) 529 | elseif s:mymatch(usercmd, '\v :call VGdb(getline('.'), 'i') 654 | imap <2-LeftMouse> 655 | imap 656 | 657 | nnoremap :call VGdb(getline('.'), 'n') 658 | nmap <2-LeftMouse> 659 | nmap 660 | 661 | inoremap 662 | "nnoremap : p: 663 | 664 | nmap :call VGdb_toggle(0) 665 | nmap :call VGdb_toggle(1) 666 | nmap ju :call VGdb_jump() 667 | nmap :call VGdb_jump() 668 | nmap :call VGdb_runToCursur() 669 | " nmap :call VGdb("run") 670 | nmap :VGdb .p 671 | vmap y:VGdb .p 0 672 | nmap pr :VGdb p 673 | vmap pr y:VGdb p 0 674 | nmap bt :VGdb bt 675 | 676 | map :VGdb .c 677 | map :VGdb k 678 | map :VGdb n 679 | map :VGdb s 680 | map :VGdb finish 681 | 682 | amenu VGdb.Toggle\ breakpointF9 :call VGdb_toggle(0) 683 | amenu VGdb.Run/ContinueF5 :VGdb c 684 | amenu VGdb.Step\ intoF11 :VGdb s 685 | amenu VGdb.NextF10 :VGdb n 686 | amenu VGdb.Step\ outShift-F11 :VGdb finish 687 | amenu VGdb.Run\ to\ cursorCtrl-F10 :call VGdb_runToCursur() 688 | amenu VGdb.Stop\ debugging\ (Kill)Shift-F5 :VGdb k 689 | amenu VGdb.-sep1- : 690 | 691 | amenu VGdb.Show\ callstack\\bt :call VGdb("where") 692 | amenu VGdb.Set\ next\ statement\ (Jump)Ctrl-Shift-F10\ or\ \\ju :call VGdb_jump() 693 | amenu VGdb.Top\ frame :call VGdb("frame 0") 694 | amenu VGdb.Callstack\ up :call VGdb("up") 695 | amenu VGdb.Callstack\ down :call VGdb("down") 696 | amenu VGdb.-sep2- : 697 | 698 | amenu VGdb.Preview\ variableCtrl-P :VGdb .p 699 | amenu VGdb.Print\ variable\\pr :VGdb p 700 | amenu VGdb.Show\ breakpoints :VGdb info breakpoints 701 | amenu VGdb.Show\ locals :VGdb info locals 702 | amenu VGdb.Show\ args :VGdb info args 703 | amenu VGdb.Quit :VGdb q 704 | 705 | if has('balloon_eval') 706 | set bexpr=VGdb_balloonExpr() 707 | set balloondelay=500 708 | set ballooneval 709 | endif 710 | endf 711 | 712 | function! VGdb_balloonExpr() 713 | return VGdb_call('.p '.v:beval_text) 714 | " return 'Cursor is at line ' . v:beval_lnum . 715 | " \', column ' . v:beval_col . 716 | " \ ' of file ' . bufname(v:beval_bufnr) . 717 | " \ ' on word "' . v:beval_text . '"' 718 | endf 719 | 720 | function! VGdb_foldTextExpr() 721 | return getline(v:foldstart) . ' ' . substitute(getline(v:foldstart+1), '\v^\s+', '', '') . ' ... (' . (v:foldend-v:foldstart-1) . ' lines)' 722 | endfunction 723 | 724 | " if the value is a pointer ( var = 0x...), expand it by "VGdb .p *var" 725 | " e.g. $11 = (CDBMEnv *) 0x387f6d0 726 | " e.g. 727 | " (CDBMEnv) $22 = { 728 | " m_pTempTables = 0x37c6830, 729 | " ... 730 | " } 731 | function! VGdb_expandPointerExpr() 732 | if ! s:mymatch(getline('.'), '\v((\$|\w)+) \=.{-0,} 0x') 733 | return 0 734 | endif 735 | let cmd = s:match[1] 736 | let lastln = line('.') 737 | while 1 738 | normal [z 739 | if line('.') == lastln 740 | break 741 | endif 742 | let lastln = line('.') 743 | 744 | if ! s:mymatch(getline('.'), '\v(([<>$]|\w)+) \=') 745 | return 0 746 | endif 747 | " '<...>' means the base class. Just ignore it. Example: 748 | " (OBserverDBMCInterface) $4 = { 749 | " <__DBMC_ObserverA> = { 750 | " members of __DBMC_ObserverA: 751 | " m_pEnv = 0x378de60 752 | " }, } 753 | 754 | if s:match[1][0:0] != '<' 755 | let cmd = s:match[1] . '.' . cmd 756 | endif 757 | endwhile 758 | " call append('$', cmd) 759 | exec "VGdb .p *" . cmd 760 | if foldlevel('.') > 0 761 | " goto beginning of the fold and close it 762 | normal [zzc 763 | " ensure all folds for this var are closed 764 | foldclose! 765 | endif 766 | return 1 767 | endf 768 | "}}} 769 | 770 | " ====== commands {{{ 771 | command! -nargs=* -complete=file VGdb :call VGdb() 772 | ca gdb VGdb 773 | ca Gdb VGdb 774 | " directly show result; must run after VGdb is running 775 | command! -nargs=* -complete=file VGdbcall :echo VGdb_call() 776 | "}}} 777 | " vim: set foldmethod=marker : 778 | -------------------------------------------------------------------------------- /vgdb_install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BIN=/usr/local/bin 4 | VIMFILES=/usr/share/vim/site 5 | 6 | echo -n "binary dir? ($BIN) " ; read opt 7 | [ -n "$opt" ] && BIN=$opt 8 | 9 | echo -n "vimfiles dir? ($VIMFILES) " ; read opt 10 | [ -n "$opt" ] && VIMFILES=$opt 11 | 12 | cp ./vgdb $BIN/ 13 | chmod a+x $BIN/vgdb 14 | #cp ./libvgdbc.so /usr/lib64 15 | cp ./vgdb.vim $VIMFILES/plugin/vgdb.vim 16 | cp ./__README__.txt $VIMFILES/doc/vgdb.txt 17 | 18 | echo -n "view doc? (=y/n) " ; read opt 19 | [ -n "$opt" ] || opt=y 20 | 21 | if [[ $opt == "y" ]]; then 22 | vim -c "helptags $VIMFILES/doc | h vgdb.txt | only" 23 | else 24 | vim -c "helptags $VIMFILES/doc | q" 25 | echo done! 26 | fi 27 | -------------------------------------------------------------------------------- /vgdb_install_mswin.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal enableextensions enabledelayedexpansion 4 | 5 | ::set BIN=c:\windows\system32 6 | set BIN=d:\tempbin 7 | set VIMFILES=D:\vim\vimfiles 8 | 9 | gdb -v >NUL 2>NUL 10 | if not %errorlevel%==0 ( 11 | echo ERROR: 'gdb' does not in binary path! 12 | goto :END 13 | ) 14 | 15 | gvim -c q 2>NUL 16 | if not %errorlevel%==0 ( 17 | echo ERROR: 'gvim' does not in binary path! 18 | goto :END 19 | ) 20 | 21 | set PERL=perl 22 | :try_perl 23 | %PERL% -e0 2>NUL 24 | if not %errorlevel%==0 ( 25 | set opt= 26 | set /p opt="Full path for perl.exe? " 27 | if not "!opt!"=="" set PERL=!opt! 28 | goto try_perl 29 | ) 30 | 31 | set opt= 32 | set /p opt="binary dir? (%BIN%) " 33 | if not "%opt%"=="" set BIN=%opt% 34 | 35 | set opt= 36 | set /p opt="vimfiles dir? (%VIMFILES%) " 37 | if not "%opt%"=="" set VIMFILES=%opt% 38 | 39 | rem vgdb.bat and vgdbc.dll MUST in the search path, e.g. c:\windows\system32 40 | echo @%PERL% %CD%\vgdb %%* > %BIN%\vgdb.bat 41 | :: copy vgdb.bat %BIN%\ 42 | copy vgdbc.dll %BIN%\ 43 | 44 | rem copy the plugin and doc to your vim folder 45 | copy vgdb.vim %VIMFILES%\plugin\vgdb.vim 46 | copy __README__.txt %VIMFILES%\doc\vgdb.txt 47 | 48 | set opt=y 49 | set /p opt="view doc? (=y/n) " 50 | if "%opt%"=="y" ( 51 | gvim -c "helptags %VIMFILES%\doc | h vgdb.txt | only" 52 | ) else ( 53 | gvim -c "helptags %VIMFILES%\doc | q" 54 | echo done. 55 | ) 56 | 57 | :END 58 | pause 59 | -------------------------------------------------------------------------------- /vgdbc.c: -------------------------------------------------------------------------------- 1 | /* 2 | Usage in VIM: 3 | On MSWin (use MinGW gcc to build): 4 | make 5 | (put the .dll in the search path) 6 | :echo libcall('vgdbc.dll', 'tcpcall', 'test') 7 | (show "OK" if successful) 8 | 9 | On Linux: 10 | make -f Makefile.linux 11 | put the .so in the lib path, e.g. /lib64 or /usr/lib64, OR set LD_LIBRARY_PATH path: 12 | $ export LD_LIBRARY_PATH={libvgdbc.so path} 13 | then run it in VIM: 14 | :echo libcall('libvgdbc.so', 'tcpcall', 'test') 15 | */ 16 | #ifdef _WINDOWS 17 | #include 18 | #else // _LINUX 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | //#define __USE_GNU 25 | #include 26 | 27 | typedef struct sockaddr_in SOCKADDR_IN; 28 | typedef struct sockaddr SOCKADDR; 29 | typedef int SOCKET; 30 | #define closesocket(x) close(x) 31 | #define WSACleanup() 32 | 33 | #define SOCKET_ERROR -1 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | /* 41 | for VIM libcall/libcallnr. 42 | Limitation: 43 | 1. cannot access VIM var or envvar 44 | 2. each call is stateless (VIM load it, exec it and then unload), cannot store state in global vars. 45 | */ 46 | 47 | #define ERR_RET(eno, estr) do {sprintf(RETBUF, "VGdbc Error [%d]: %s", eno, estr); return RETBUF;} while (0) 48 | 49 | void *g_hModule; 50 | 51 | static int BUFLEN = 4000; 52 | static char *RETBUF; 53 | 54 | struct InitLib 55 | { 56 | InitLib(); 57 | ~InitLib(); 58 | } g_initobj; 59 | 60 | InitLib::InitLib() 61 | { 62 | if (RETBUF == NULL) 63 | { 64 | #ifdef _WINDOWS 65 | g_hModule = LoadLibrary("vgdbc.dll"); 66 | #else // _LINUX 67 | Dl_info di; 68 | if (dladdr(RETBUF, &di)) { 69 | g_hModule = dlopen(di.dli_fname, RTLD_LAZY); 70 | } 71 | #endif 72 | RETBUF = (char*)malloc(BUFLEN); 73 | } 74 | } 75 | 76 | InitLib::~InitLib() 77 | { 78 | free(RETBUF); 79 | } 80 | 81 | extern "C" { 82 | 83 | const char *test(const char *cmd) 84 | { 85 | static char lastcmd[100]; 86 | char *portstr = getenv("VGDB_PORT"); 87 | sprintf(RETBUF, "%s OK. $VGDB_PORT=%s. last test='%s'", cmd, portstr, lastcmd); 88 | strncpy(lastcmd, cmd, 100); 89 | return RETBUF; 90 | } 91 | 92 | const char *tcpcall(const char *cmd) 93 | { 94 | int VGDB_PORT = 30899; 95 | SOCKET SOCK; 96 | 97 | int ret; 98 | strcpy(RETBUF, "OK"); 99 | 100 | #ifdef _WINDOWS 101 | WSADATA wsaData; 102 | WORD wVersionRequested = MAKEWORD(2, 2); 103 | 104 | ret = WSAStartup(wVersionRequested, &wsaData); 105 | if (ret != 0) 106 | { 107 | ERR_RET(-1, "WSAStartup"); 108 | } 109 | #endif 110 | 111 | SOCK = socket(AF_INET, SOCK_STREAM, 0); 112 | if (SOCK < 0) 113 | { 114 | WSACleanup(); 115 | ERR_RET(-1, "fail to create socket"); 116 | } 117 | char *portstr = getenv("VGDB_PORT"); 118 | if (portstr != NULL) { 119 | int port = atoi(portstr); 120 | if (port > 0) { 121 | VGDB_PORT = port; 122 | } 123 | } 124 | 125 | SOCKADDR_IN addrSrv; 126 | addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1"); 127 | addrSrv.sin_family = AF_INET; 128 | addrSrv.sin_port = htons(VGDB_PORT); 129 | 130 | ret = connect(SOCK, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); 131 | if (SOCKET_ERROR == ret) 132 | { 133 | closesocket(SOCK); 134 | WSACleanup(); 135 | ERR_RET(ret, "fail to connect VGdb."); 136 | } 137 | sprintf(RETBUF, "%s\n", cmd); 138 | ret = send(SOCK, RETBUF, strlen(RETBUF), 0); 139 | if (SOCKET_ERROR == ret) 140 | { 141 | closesocket(SOCK); 142 | WSACleanup(); 143 | ERR_RET(ret, "fail to send cmd"); 144 | } 145 | 146 | int cnt = 0; 147 | char *p = RETBUF; 148 | int totalcnt = 0; 149 | while (1) { 150 | cnt=recv(SOCK, p, BUFLEN-totalcnt-1, 0); 151 | if (cnt <= 0) 152 | break; 153 | p += cnt; 154 | totalcnt += cnt; 155 | if (BUFLEN <= totalcnt+1) { 156 | char *tmpbuf = (char*)realloc(RETBUF, BUFLEN <<1); 157 | if (tmpbuf == NULL) { 158 | while (recv(SOCK, RETBUF-100, 100, 0) > 0); // clear the send buf of the server. 159 | strcpy(RETBUF-100, " ... (too long)\n"); 160 | break; 161 | } 162 | RETBUF = tmpbuf; 163 | p = RETBUF +BUFLEN-1; 164 | BUFLEN <<=1; 165 | } 166 | } 167 | if (totalcnt >= 0) 168 | RETBUF[totalcnt] = 0; 169 | closesocket(SOCK); 170 | WSACleanup(); 171 | return RETBUF; 172 | } 173 | 174 | } // extern "C" 175 | 176 | int main(int argc, char *argv[]) 177 | { 178 | if (argc <= 1) { 179 | printf("Usage: %s {gdbcmd}\n", argv[0]); 180 | return -1; 181 | } 182 | const char *ret = tcpcall(argv[1]); 183 | printf("%s\n", ret); 184 | return 0; 185 | } 186 | 187 | -------------------------------------------------------------------------------- /vgdbc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyshore2001/vgdb-vim/64e10cd8add15b60a410239aec0b2950e0afaf0c/vgdbc.dll --------------------------------------------------------------------------------