Hacking on The Core

Some techniques for Hacking on The Core are described below.

See Also

Tcl/Tk Engineering Manual
A manual for the development of C code for Tcl/Tk.
Re: PATCH kill access_ok() call from copy_siginfo_to_user() that we might as well avoid. , Linus Torvalds, 2014-02-28
In which Linus encourages painting the bikeshed.

Navigation Tips

Here's a dirty way to hunt down where a command, e.g. file size, is implemented.

  • file size is part of an ensemble, so the command will be registered somewhere with the name "size" .. let's start with
grep '"size"' */*.{c,h}
  • Searching for that string, including the quotes, leads quickly to generic/tclCmdAH.c : FileAttrSizeCmd looks like the right function.
  • A search for that name at the beginning of a line leads to the right file:
grep FileAttrSizeCmd */*
  • FileAttrSizeCmd is defined in the same file, and calls GetStatBuf, which calls its statProc argument, which is initialised with a pointer to Tcl_FSStat in tclIOUtil.c

So far, so simple .. but at this point it looks a bit hairy, and ctags has stopped helping. Fortunately, there are naming conventions to help here:

A function like Tcl_FSStat whose sole job is to dispatch through a struct to an implementation function also needs a typedef to exist somewhere, defining the signature of the function it's calling. In this case, we can infer a type signature for fsPtr->statProc, but we never see what it is called. We *could* look at the declaration of Tcl_Filesystem, find its statProc field and look for other things declared with that name ...

but there's a shortcut: the typedef for Tcl_FSStat is Tcl_FSStatProc, and we are looking for something declared a Tcl_FSStatProc. So search for it .. at the beginning of a line: grep ^Tcl_FSStatProc */*.c

  generic/tclIOUtil.c:132:Tcl_FSStatProc                      TclpObjStat;

Tclp indicates a private, platform-specific function, which will be declared for example in unix/tclUnixFile.c , and calls a trivial wrapper for stat(2). Simple!

This pattern of a wrapper function named Tcl_Foo calling its implementation through a pointer typed Tcl_FooProc is quite common across the Tcl core, so it's a useful shortcut to remember.

DKF: Commands are mostly implemented in tclCmd??.c. The bytecoded versions are compiled in tclCompCmds.c or tclCompCmds??.c (mostly by command name, but just search; the names are usually "obvious" and consistent). The bytecode engine is entirely in tclExecute.c (and is not an easy place to start). Some commands are elsewhere (tclVar.c for variable/stack-frame commands, tclProc.c for proc, tclIOCmd.c for a bunch of ones to do with I/O, tclNamesp.c for virtually everything to do with namespaces, etc.) The quickest way to find where a command is is usually to look at the mapping table in tclBasic.c and search for the function name; grepping gets you to the right spot rapidly, especially since function definitions are the only places where function names appear at the start of the line in code following Tcl's Engineering Manual. Not everything is in that table (most ensembles aren't in 8.6) but they're still a minority of top-level commands.

Some Debugging Macros

PYK 2015-04-25: I'm still learning the ropes of C, but I'll throw these here anyway. They came in handy recently to debug an I/O/thread issue.

#define tdebug(statement) \
    fprintf(stderr, "%p: %s\n", Tcl_GetCurrentThread(), (statement))

#define tcdebug(statePtr, statement) \
    fprintf(stderr, "%p: chan %s: %s\n", Tcl_GetCurrentThread(), (statePtr)->channelName, (statement))

AMG: Minor, minor style tweaks, though the macros look good and useful for situations where you can't use the Tcl I/O or command return mechanisms. The do ... while (0) trick is not necessary when the macro is just a single function call. Arguments to be expanded almost always should be surrounded by parentheses, and you do this, but you had (statePtr->channelName) rather than (statePtr)->channelName.

DKF: The do...while(0) trick is usually entirely unnecessary with Tcl's style... but it is recommended anyway. But it's not so important for debugging things.

I tend to use // style comments when I'm putting in things that are not meant to hit production, since Tcl never uses them and that makes it easy to audit for stuff that I need to remove before committing.

Reference Counts

PYK 2018-03-13: Watch the refCount member of Tcl_Obj and Object to get a play-by-play account of who is hanging onto and letting go of an object. When a segmentation fault occurs in a Tcl compiled with memory debugging, the structure the refCount belongs to will often be filled with the "deallocated memory" pattern 0x6161616...., which indicates that the structure was freed and someone subsequently attempted to access it.