Version 5 of Hacking on The Core

Updated 2016-07-20 20:01:00 by dkf

Hacking on The Core — it's good for the soul!

See Also

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 (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"' */*.ch
  • searching for that string (including quotes!) leads quickly to generic/tclCmdAH.c : FileAttrSizeCmd looks like what we need!
  • we can navigate quickly to the declaration site of functions by looking for the function name at the beginning of a line: 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, 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.