Insertion of tracing code using the C++ preprocessor

A few months back I got this wild idea that you could insert trace-logging code (not jit-tracing, that's something else) into more or less arbitrary C++ by redefining certain C++ keywords as macros. It turns out that you can, in fact, make this work, at least for certain non-trivial cases. Notably, I've been able to compile an instrumented version of Firefox on OS X using this technique.

I'm not sure whether the technique is generally useful, but given our new focus on crashes and hangs, I thought I'd at least mention it. It's probably not particularly useful for crashes but it may have some utility for debugging hangs. I used an earlier version of this code to debug a Firebug hang (Bug 497028), although in that case I already had a good idea where the problem was and I probably could have used the debugger just as effectively.

Here's the latest tracing code, which I have in a file called "quicktrace.h":

#ifdef __cplusplus    #include <stdio.h>    #include <string.h>    static void QuickTrace(const char *fileName, int lineNumber)    {      if (lineNumber % 100 != 0) return;//    if (strstr(fileName, "/js/") != NULL) return;      fprintf(stderr, ";;; %s %d\n", fileName, lineNumber);    }    #define if     if (QuickTrace(__FILE__, __LINE__), 0) {throw 0;} else if    #define for    if (0) {throw 0;} else for    #define switch if (0) {throw 0;} else switch    #define do     if (0) {throw 0;} else do    #endif

I inserted this code globally by adding the following two lines to my .mozconfig:

export CXXFLAGS="-include /Users/cbartley/dev/mozilla-e/src/quicktrace.h"  ac_add_options --enable-cpp-exceptions

Note that the use of exceptions is non-functional -- the only purpose is to suppress warnings about code paths that don't return a value. If you don't care about the warnings you can dispense with the "throw 0" statements in the header file and the --enable-cpp-exceptions in your .mozconfig file.

Notes:

  • This is unquestionably an abuse of the preprocessor. Don't go down this route unless you really think you know what you're doing.
  • If you really want to create an instrumented build, there are better ways to do it, for example Taras Glek's "Pork" tool. Even using shell scripts and sed to create an instrumented source tree might be a better choice.
  • I think the code in this post will probably work on any GCC-based build, but I don't know for sure. As far as I can tell, Visual C++ doesn't have an equivalent of GCC's "-include" flag and I haven't tried this on Windows.
  • To do useful trace logging you really want a global implementation of the "QuickTrace" function. This is somewhat challenging for Firefox because of the way it's linked. I'm leaving this part as an exercise to the reader.
  • The Firefox build actually compiles some tools from source and then uses those tools later on in the build. This means that if you're going to generate logging from the trace function you do not want to send it to stdout.
  • It might make more sense to include the quicktrace.h header file from another very commonly used header file. This might be the only option on Windows anyway.
  • I think it should be pretty easy to build an instrumented Firefox using this approach and the Try server. I haven't actually attempted this yet, though.
  • If you generate output every time the trace function is called, it will be unbearably slow.
  • You can't generate a proper call graph using this approach, although with some post-processing you could probably get a useful approximation of a call graph.
  • There's probably some caveat that I forgot to mention.