'''Hacking on The Core''' — it's good for the soul! ** See Also ** [https://lkml.org/lkml/2004/12/20/255%|%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 http://core.tcl.tk/tcl/artifact/8d45dfc70e9d36c1?ln=977%|%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 http://core.tcl.tk/tcl/artifact/e70f605d466cd972?ln=2113-2125%|%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` ======c generic/tclIOUtil.c:132:Tcl_FSStatProc TclpObjStat; ====== `Tclp` indicates a private, platform-specific function, which will be declared for example in http://core.tcl.tk/tcl/artifact/423adfd5425eebe3?ln=841-852%|%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. ======c #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. <> The Tcl Core | Debugging