From 8db6611c4c31932335de5a0c12de2a48830859b4 Mon Sep 17 00:00:00 2001 From: andreas_kupries Date: Mon, 11 Aug 2008 20:13:39 +0000 Subject: * generic/tclProc.c (Tcl_ProcObjCmd): Fixed memory leak triggered * tests/proc.test: by procbody::test::proc. See [Bug 2043636]. Added a test case demonstrating the leak before the fix. Fixed a few spelling errors in test descriptions as well. --- ChangeLog | 7 +++++++ generic/tclProc.c | 29 ++++++++++++++++++++++++----- tests/proc.test | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 023241f..030f11c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2008-08-11 Andreas Kupries + + * generic/tclProc.c (Tcl_ProcObjCmd): Fixed memory leak triggered + * tests/proc.test: by procbody::test::proc. See [Bug 2043636]. + Added a test case demonstrating the leak before the fix. Fixed a + few spelling errors in test descriptions as well. + 2008-07-28 Andreas Kupries * generic/tclBasic.c: Added missing release of extended command diff --git a/generic/tclProc.c b/generic/tclProc.c index b2c2b8d..950d448 100644 --- a/generic/tclProc.c +++ b/generic/tclProc.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclProc.c,v 1.44.2.8 2008/07/21 19:37:45 andreas_kupries Exp $ + * RCS: @(#) $Id: tclProc.c,v 1.44.2.9 2008/08/11 20:13:43 andreas_kupries Exp $ */ #include "tclInt.h" @@ -182,7 +182,8 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv) context.line && (context.nline >= 4) && (context.line [3] >= 0)) { - int new; + int isNew; + Tcl_HashEntry* hePtr; CmdFrame* cfPtr = (CmdFrame*) ckalloc (sizeof (CmdFrame)); cfPtr->level = -1; @@ -206,9 +207,27 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv) cfPtr->cmd.str.cmd = NULL; cfPtr->cmd.str.len = 0; - Tcl_SetHashValue (Tcl_CreateHashEntry (iPtr->linePBodyPtr, - (char*) procPtr, &new), - cfPtr); + hePtr = Tcl_CreateHashEntry (iPtr->linePBodyPtr, (char*) procPtr, + &isNew); + if (!isNew) { + /* + * Get the old command frame and release it. See also + * TclProcCleanupProc in this file. Currently it seems as if + * only the procbodytest::proc command of the testsuite is + * able to trigger this situation. + */ + + CmdFrame* cfOldPtr = (CmdFrame *) Tcl_GetHashValue(hePtr); + + if (cfOldPtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(cfOldPtr->data.eval.path); + cfOldPtr->data.eval.path = NULL; + } + ckfree((char *) cfOldPtr->line); + cfOldPtr->line = NULL; + ckfree((char *) cfOldPtr); + } + Tcl_SetHashValue (hePtr, cfPtr); } } #endif diff --git a/tests/proc.test b/tests/proc.test index 222a31f..8a9ec14 100644 --- a/tests/proc.test +++ b/tests/proc.test @@ -13,13 +13,15 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: proc.test,v 1.11.2.1 2004/05/02 21:07:16 msofer Exp $ +# RCS: @(#) $Id: proc.test,v 1.11.2.2 2008/08/11 20:13:44 andreas_kupries Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { package require tcltest namespace import -force ::tcltest::* } +testConstraint memory [llength [info commands memory]] + catch {eval namespace delete [namespace children :: test_ns_*]} catch {rename p ""} catch {rename {} ""} @@ -237,7 +239,7 @@ test proc-4.3 {TclCreateProc, procbody obj, too many args} { set result } {procedure "t": arg list contains 3 entries, precompiled header expects 1} -test proc-4.4 {TclCreateProc, procbody obj, inconsitent arg name} { +test proc-4.4 {TclCreateProc, procbody obj, inconsistent arg name} { catch { proc p {x y z} { set v [join [list $x $y $z]] @@ -254,7 +256,7 @@ test proc-4.4 {TclCreateProc, procbody obj, inconsitent arg name} { set result } {procedure "t": formal parameter 1 is inconsistent with precompiled body} -test proc-4.5 {TclCreateProc, procbody obj, inconsitent arg default type} { +test proc-4.5 {TclCreateProc, procbody obj, inconsistent arg default type} { catch { proc p {x y {z Z}} { set v [join [list $x $y $z]] @@ -271,7 +273,7 @@ test proc-4.5 {TclCreateProc, procbody obj, inconsitent arg default type} { set result } {procedure "t": formal parameter 2 is inconsistent with precompiled body} -test proc-4.6 {TclCreateProc, procbody obj, inconsitent arg default type} { +test proc-4.6 {TclCreateProc, procbody obj, inconsistent arg default type} { catch { proc p {x y z} { set v [join [list $x $y $z]] @@ -288,7 +290,7 @@ test proc-4.6 {TclCreateProc, procbody obj, inconsitent arg default type} { set result } {procedure "t": formal parameter 2 is inconsistent with precompiled body} -test proc-4.7 {TclCreateProc, procbody obj, inconsitent arg default value} { +test proc-4.7 {TclCreateProc, procbody obj, inconsistent arg default value} { catch { proc p {x y {z Z}} { set v [join [list $x $y $z]] @@ -305,6 +307,30 @@ test proc-4.7 {TclCreateProc, procbody obj, inconsitent arg default value} { set result } {procedure "t": formal parameter "z" has default value inconsistent with precompiled body} +test proc-4.8 {TclCreateProc, procbody obj, no leak on multiple iterations} -setup { + proc getbytes {} { + set lines [split [memory info] "\n"] + lindex $lines 3 3 + } + proc px x { + set y [string tolower $x] + return "$x:$y" + } + px x +} -constraints memory -body { + set end [getbytes] + for {set i 0} {$i < 5} {incr i} { + + procbodytest::proc tx x px + + set tmp $end + set end [getbytes] + } + set leakedBytes [expr {$end - $tmp}] +} -cleanup { + rename getbytes {} +} -result 0 + test proc-5.1 {Bytecompiling noop; test for correct argument substitution} { proc p args {} ; # this will be bytecompiled into t proc t {} { -- cgit v0.12