From 93449e42cf54ab52e93b037ccea2ff43cb991181 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 1 Dec 2025 13:53:49 +0800 Subject: [PATCH] Memory leak with :breakadd expr Problem: Memory leak with :breakadd expr. Solution: Free debug_oldval and debug_newval before assigning to them. It seems that :breakadd expr doesn't work as documented at all. This PR only fixes the memory leak. The tests are for the existing behavior. --- src/debugger.c | 6 ++++ src/testdir/test_debugger.vim | 55 ++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/debugger.c b/src/debugger.c index 97d54c439404d2..f553a8e6a6fc7c 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -1111,8 +1111,10 @@ debuggy_find( { if (bp->dbg_val == NULL) { + vim_free(debug_oldval); debug_oldval = typval_tostring(NULL, TRUE); bp->dbg_val = tv; + vim_free(debug_newval); debug_newval = typval_tostring(bp->dbg_val, TRUE); line = TRUE; } @@ -1129,10 +1131,12 @@ debuggy_find( typval_T *v; line = TRUE; + vim_free(debug_oldval); debug_oldval = typval_tostring(bp->dbg_val, TRUE); // Need to evaluate again, typval_compare() overwrites // "tv". v = eval_expr_no_emsg(bp); + vim_free(debug_newval); debug_newval = typval_tostring(v, TRUE); free_tv(bp->dbg_val); bp->dbg_val = v; @@ -1142,7 +1146,9 @@ debuggy_find( } else if (bp->dbg_val != NULL) { + vim_free(debug_oldval); debug_oldval = typval_tostring(bp->dbg_val, TRUE); + vim_free(debug_newval); debug_newval = typval_tostring(NULL, TRUE); free_tv(bp->dbg_val); bp->dbg_val = NULL; diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index 264420998e58a3..aa014c43423753 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -361,34 +361,73 @@ func Test_Debugger_breakadd() endfunc " Test for expression breakpoint set using ":breakadd expr " +" FIXME: This doesn't seem to work as documented. The breakpoint is not +" triggered until the next function call. func Test_Debugger_breakadd_expr() CheckCWD let lines =<< trim END + func Foo() + eval 1 + eval 2 + endfunc + let g:Xtest_var += 1 + call Foo() + let g:Xtest_var += 1 + call Foo() END - call writefile(lines, 'XdebugBreakExpr.vim', 'D') + call writefile(lines, 'XbreakExpr.vim', 'D') " Start Vim in a terminal - let buf = RunVimInTerminal('XdebugBreakExpr.vim', {}) + let buf = RunVimInTerminal('XbreakExpr.vim', {}) call s:RunDbgCmd(buf, ':let g:Xtest_var = 10') call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var') - call s:RunDbgCmd(buf, ':source %') let expected =<< trim eval END Oldval = "10" Newval = "11" - {fnamemodify('XdebugBreakExpr.vim', ':p')} - line 1: let g:Xtest_var += 1 + {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo + line 1: eval 1 END call s:RunDbgCmd(buf, ':source %', expected) - call s:RunDbgCmd(buf, 'cont') let expected =<< trim eval END Oldval = "11" Newval = "12" - {fnamemodify('XdebugBreakExpr.vim', ':p')} - line 1: let g:Xtest_var += 1 + {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo + line 1: eval 1 + END + call s:RunDbgCmd(buf, 'cont', expected) + call s:RunDbgCmd(buf, 'cont') + + " Check the behavior without the g: prefix. + " FIXME: The Oldval and Newval don't look right here. + call s:RunDbgCmd(buf, ':breakdel *') + call s:RunDbgCmd(buf, ':breakadd expr Xtest_var') + let expected =<< trim eval END + Oldval = "13" + Newval = "(does not exist)" + {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo + line 1: eval 1 END call s:RunDbgCmd(buf, ':source %', expected) + let expected =<< trim eval END + {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo + line 2: eval 2 + END + call s:RunDbgCmd(buf, 'cont', expected) + let expected =<< trim eval END + Oldval = "14" + Newval = "(does not exist)" + {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo + line 1: eval 1 + END + call s:RunDbgCmd(buf, 'cont', expected) + let expected =<< trim eval END + {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo + line 2: eval 2 + END + call s:RunDbgCmd(buf, 'cont', expected) + call s:RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) endfunc