Tcl Source Code

View Ticket
Login
Ticket UUID: 467523
Title: memory leak in [namespace eval foo {}]
Type: Bug Version: obsolete: 8.4a3
Submitter: rbrunner Created on: 2001-10-03 15:47:27
Subsystem: 47. Bytecode Compiler Assigned To: msofer
Priority: 8 Severity:
Status: Closed Last Modified: 2004-07-16 00:42:21
Resolution: Fixed Closed By: msofer
    Closed on: 2001-10-12 16:15:28
Description:
The following code leaks (in every Tcl version):

  interp create foo 
  foo eval {
      namespace eval bar {}
  }
  interp delete foo

Note: 

  interp create foo 
  foo eval {
      namespace eval bar {\#}
  }
  interp delete foo

does NOT leak.

Unfortunately I didn't really track down the problem, 
but the call 

  result = Tcl_EvalObjEx(interp, objv[3], 0);

in generic/tclNamesp.c line 2955 (tcl8.4.a3), which is 
called with an empty string in objv[3] seems to cause 
the leak. Changing this code to 

  if (strlen(Tcl_GetString(objv[3])))
    result = Tcl_EvalObjEx(interp, objv[3], 0);
  else
    result = TCL_OK;

fixes the symptoms, but certainly not the problem.
(Sorry for not having a proper fix.)
User Comments: msofer added on 2004-07-16 00:42:21:
Logged In: YES 
user_id=148712

This bug reappeared with the (faulty) fix of bug #983660 (in
tcl8.5a2), and was fixed using a different strategy.

The previous strategy was to have special code in
TclReleaseLiteral to handle the self-references generated by
empty scripts. The new approach avoids the self-reference
altogether, by having empty scripts return an unshared literal.

msofer added on 2001-10-12 23:15:29:

File Added - 11886: leak.tests

msofer added on 2001-10-12 23:15:28:
Logged In: YES 
user_id=148712

Added tests in tests/compile.test - they are only effective
if tcl was compiled with TCL_MEM_DEBUG

msofer added on 2001-10-12 05:37:17:
Logged In: YES 
user_id=148712

Patch committed to HEAD and core-8-3-1-branch

msofer added on 2001-10-11 20:13:15:

File Deleted - 11528: 



File Added - 11851: leak.patch

msofer added on 2001-10-11 20:13:14:
Logged In: YES 
user_id=148712

I do think I have a correct patch now - the special handling
of self-referencing bytecodes in TclreleaseLiteral
(tclLiteral.c) was being done too early (entryPtr->refCount
== 2 at function entry) and removing two global table
references in one go (setting entryPtr->refCount to 0) while
decreasing the object's corresponding refCount by just one.

My earlier patch corrected this by removing a reference to
the object too - as Ronnie discovered, in some situations
this caused the object to be accessed after it was removed.

This patch restores "normal handling": each call to
TclReleaseLiteral removes at most one reference in the
global table, and the special code for self-referencing
bytecodes is only used at the last pass.

msofer added on 2001-10-10 00:03:01:
Logged In: YES 
user_id=148712

Dump confirmed on linux; I am removing the patch from HEAD
and core-8-3-1-branch

rbrunner added on 2001-10-09 23:38:20:
Logged In: YES 
user_id=205230

Here a complete log of what I'm doing. Note: running the 
code in an interactive tclsh works (no dump) and not 
changing the MEM_DEBUG_FLAGS in Makefile *ALSO* works (no 
dump). Let me know whether you need the tclConfig.sh or any 
other additional info ...

> uname -a
SunOS mlt 5.6 Generic_105181-26 sun4u sparc SUNW,Ultra-250
> tar xzf tcl8.4a3.tar.gz
> cd tcl8.4a3/generic
>

# apply patch to tclLiteral.c (just copy 1.9 over 1.8)

> cd ../unix
./configure
>

# change Makefile to memory debug
    #MEM_DEBUG_FLAGS =
    MEM_DEBUG_FLAGS = -DTCL_MEM_DEBUG

> make
> setenv LD_LIBRARY_PATH .
>

# write a mini script foo: vi foo
    #!./tclsh
    for {set a 0} {$a < 10} {incr a} {
namespace eval bar {}
namespace delete bar
    }

> chmod +x foo
> ./foo
file = ./../generic/tclExecute.c, line = 663
Trying to increment refCount of previously disposed object.
Abort (core dumped)
>

msofer added on 2001-10-09 23:02:53:
Logged In: YES 
user_id=148712

Ronnie: Yes, the patch only adds one loc to tclLiteral.c

I do not see any problems with that code in either 8.4a3,
8.4a4 or the 8-3-1-branch code - your script does not
trigger a dump on my setup.

Could you help me reproduce the dump? I'd need to know 
  - configure and compile flags
  - how you are running the script: tclsh? tkcon? from a
file?
  - anything else?

Thanks

rbrunner added on 2001-10-09 22:39:42:
Logged In: YES 
user_id=205230

Miguel: did you only patch tclLiteral.c? Compiling with the 
latest version of this file (1.9, same as 1.8.2.1) tcl8.4a3 
crashes running the following code:

for {set a 0} {$a < 10} {incr a} {
    namespace eval bar {}
    namespace delete bar
}

my tclsh dumps core with

  file = ./../generic/tclExecute.c, line = 663
  Trying to increment refCount of previously disposed 
object.
  Abort (core dumped)

msofer added on 2001-10-06 08:05:05:
Logged In: YES 
user_id=148712

Patch committed to HEAD and core-8-3-1-branch

msofer added on 2001-10-06 07:02:03:

File Added - 11620: leak.patch

Logged In: YES 
user_id=148712

The attached leak.patch to generic/tclLiteral.c
(TclReleaseLiteral) solves the issue.

msofer added on 2001-10-05 20:37:05:
Logged In: YES 
user_id=148712

The problem (I think) is that the object referred to by the
literal "" in the slave interp is never freed, as it is
self-referential: the bytecode pushes itself on the stack,
so that the bytecode object always has refCount >=1 - it
appears in its own local literal table. 

The code leaks 164 bytes: 24 for the object, 128 for its
internal rep as a bytecode, 12 for the handle to the
interpreter (it would be freed by CleanupByteCode when the
object is freed).

One possible solution I am exploring is to be more careful
about sharing literals. The solution to this bug along this
path could conceivably also fix [Bug 458361] in a roundabout
way.

rbrunner added on 2001-10-05 02:27:06:
Logged In: YES 
user_id=205230

Some further observations:
Any recursive call to Tcl_EvalObjEx with an empty string 
object to eval leaks:

  time {time {}}
  time {switch 1 1 {}}
  switch 1 1 {time {}}

In addition I found out, that these calls leak, whenever 
the innermost empty code matches the Literal that is added 
in TclCompile.c line 1041 (tcl8.4a3):

  TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0)

Setting this to

  TclRegisterLiteral(envPtr, "  ", 2, /*onHeap*/ 0)

makes 

  time {time {  }}

leak. It seems that this equality increments the empty 
codes object's refCount (objPtr in Tcl_evalObjEx) to be too 
high -> it is not cleaned up properly.

msofer added on 2001-10-04 02:12:50:
Logged In: YES 
user_id=148712

No, that wasn't it ...

msofer added on 2001-10-04 00:16:57:

File Added - 11528: 467523.patch

msofer added on 2001-10-04 00:16:55:
Logged In: YES 
user_id=148712

I think the leak should occur at any evaluation of {}, ie,
also for
    proc a {} {}
    a

As I see it, the special handling of "bytecodes with empty
source" in Tcl_EvalObjEx is at fault. I enclose a small
patch that should correct this.

Ronnie, Jeff: does this fix the bug?

hobbs added on 2001-10-03 23:43:49:
Logged In: YES 
user_id=72656

Confirmed in 8.3.3.

Attachments: