NAME

traceString - Inserts a user defined string including the routine name at the start of each routine.


SYNOPSIS

traceString [ --help ] [ -h] [ --verbose ] [ -v ] [ -patternFile file] [ -p file] [ --indent ] [ -i ] [ --rtest ] [ -r ] [ --default name ] [ --remove] [ --active file] [ --inactive file] [ --pipe] [ --name ] [ --debug ] [ -d ] [ --printStates ] [ -S ] [ --version]

list_of_files_OR_directories


DESCRIPTION:

This tool reads in C/C++ source files, finds the beginning and end of each function and optionally inserts code according to a pattern file. Its primary purpose is to provide a literal string containing the name of the function, its class, or some combination thereof.

A simple example is given by:

  int X::b(int a) {}

which expands to:

  int X::b() { /* %TRACE% */ FUNC_TRACE("X::b(int a)"); /* %TRACE% */ }

Here FUNC_TRACE is the default insertion text (which should correspond to a user-defined macro). Note that the added code is place on the line with the curly braces to avoid line number changes. This is especially useful when used in conjunction with --pipe.


EXECUTION:

The tool first reads in the pattern file, then instruments each file given on the command line. If a directory is specified the tool recursively searches the directory for *.c, *.h, *.C, *.cpp, *.cc, *.hpp files to convert. (Note that it will skip files of the pattern *.new.* corresponding to generated temporary files.)

For each file, say foo.C, the tool writes the generated code to foo.new.C until the tool has completed the conversion. Then the original file is renamed to foo.C~ and the temporary file is renamed to foo.C. (See the --rtest option to change this behavior.)

A source file can be run through traceString repeatedly. The old trace strings are replaced with new ones.


PATTERN FILE:

The pattern file controls what text is inserted at the beginning of each routine. Optionally code can be added at the end of the routine as well.

The Pattern file looks like:

  [START_OF_ROUTINE_PATTERNS]
  default : FUNC_TRACE("$(FQNAME)");
  ifdef   : \nifdef DEBUG\nstatic const char* myNm = "$(NAME)";\n#endif
  short   : static const char* myNm = "$(NAME)";
  long    : static const char* myNm = "$(NAME)"; FUNC_TRACE("$(FQNAME)");
  group   : Grp("$(FQNAME)","$(GROUP)");
  None    :
  [END_OF_ROUTINE_PATTERNS]

where the [START_OF_ROUTINE_PATTERNS] starts the list of patterns to be used at the beginning of routines and [END_OF_ROUTINE_PATTERNS] obviously lists the patterns to used at the end of routines. If there are no patterns listed then no instrumentation is done for that section.

Each line is has two parts separated by a ':'. The left is the pattern name and the right is the pattern to be used. The default pattern is used (you guessed it) by default. The default pattern automatically gets the bookend trace comment /* %TRACE% */. The other patterns work similarly and are explained in Using non-default patterns.

The pattern can only be one line long, but it can contain more than one statement and can be of any length. The pattern string can contain '\n' or '\t' that will be expanded to newlines and tabs. In this way #if's can be supported as shown in the ifdef pattern.

The pattern file uses // just like C++ to mark comment lines.


Re-instrumenting:

When re-instrumenting, the code removes the old trace string code and replaces it with a new one. The tool finds the first traceString comment, interrogates it for the pattern name and arguments, and then removes all code up to and including a closing /* %TRACE% */ comment. Then a new one is created and written to the output file. Thus the /* %TRACE% */ comments must be in pairs.


Pattern Argument expansion:

Several strings are available from traceString for use in a pattern:

$(NAME) is the class and namespace names (if either apply) plus the routine name, but NOT the function arguments.

$(FQNAME) is the ``fully qualified'' name which includes $(NAME) followed by the function arguments.

$(CLASS) is just the class name (with appropriate namespace names).

So for the routine X::b(int a) in namespace NS. $(NAME) is NS::X::b and $(FQNAME) is NS::X::b(int a) and $(CLASS) is NS::X. Note that traceString only detects the use of namespaces when they are denoted using namespace NS {, not when the are denoted via using namespace NS;.


Using non default patterns.

You can choose a different pattern by changing the leading bookend to have the appropriate keyword as follows:

  int X::b(){ /* %TRACE[short]% */  /* %TRACE% */ }

which, using the pattern file given above, will be expanded to:

  int X::b(){ /* %TRACE[short]% */ static const char* myNm = "X::b"; /* %TRACE% */ }

Changing /* %TRACE% */ to /* %TRACE[short]% */ means that the short pattern is used rather than the default. In this way, a developer can control the expansion in particular routines.

Note that the None pattern can be used to essentially turn off traceString in a function. For example, the function

  int X::b(){ /* %TRACE[None]% */  /* %TRACE% */ }

is not modified by traceString.


Controlling Pattern Keyword Expansion

In particular routines one may wish to control the keyword expansion of $(NAME), $(FQNAME), $(CLASS) or a developer defined variable. This is controlled by placing the names in a comma-separated brace list. So the following

  int X::b(){ /* %TRACE[group]{GROUP="Y"}% */  /* %TRACE% */ }

would expand to:

  int X::b(){ /* %TRACE[group]{GROUP="Y"}% */ Grp("X::b()","Y"); /* %TRACE% */ }

using the pattern file given above. Here we see that the GROUP keyword has been set by the user to be ``Y'', and later expanded in the pattern text. If a keyword has no assigned value then the expansion is a zero length string.


END OF ROUTINE Behavior

It is possible to place tracing at the end of routines. Using the following as the pattern file.

  [START_OF_ROUTINE_PATTERNS]
  default : START("$(NAME)");
  None    :
  [END_OF_ROUTINE_PATTERNS]
  default : END("$(NAME)");
  None    :

Then the following code

  int bar(float x)
  {
    if (x > 0) return 1;
    ...
  }

is converted to:

  int bar(float x)
  { /* %TRACE% */ START("bar"); /* %TRACE% */
    if (x > 0)  /* %TRACE% */  do { END("bar");return 1;} while (0);  /* %TRACE% */
    ...
   /* %TRACE% */ END("bar"); /* %TRACE% */}

All of the return statements are wrapped in a do {...} while(0): pattern so that the generated code always has the correct behavior.


INDENTATION: Choosing the location of the trace string

By default the tool places the trace string on the same line as the leading {. If the --indent option is given then there are 3 possibilities: a) If there is a trace string then the same indentation is reused. b) If there is code on the same line then the trace string is added on the same line (as before). c) If there is no trace string then a new line is added and the current indentation is inserted.

Here are examples of the three cases. If the input is this:

  int X::caseA(float x)
  { /* %TRACE% */ FUNC_TRACE("X::caseA(float x)"); /* %TRACE% */ 
    //
  } 
  int X::caseB(float x) 
  {}
  int X::caseC(float x) 
  {
    //
  }

Then the output of traceString --indent ... is:

  int X::caseA(float x)
  { /* %TRACE% */ FUNC_TRACE("X::caseA(float x)"); /* %TRACE% */ 
    //
  } 
  int X::caseB(float x) 
  { /* %TRACE% */ FUNC_TRACE("X::caseB(float x)"); /* %TRACE% */}
  int X::caseC(float x) 
  {
    /* %TRACE% */ FUNC_TRACE("X::caseC(float x)"); /* %TRACE% */
    //
  }


Active/Inactive Routine List:

traceString can take a list of ``fully qualified'' routine name which can a list of active (or inactive) routine by using the --active (or --inactive) options. To generate this list of active routines it is best to first use the --name option to generate the routine name. The comparison is an exact string match so the number of spaces and name of arguments must agree completely.

Then take the generated list routines to get the desired list. Note that it makes no sense to have both an active and inactive lists used at the same time and the tool reports this as an error.


OPTIONS:

--help, -h
A short help message.

--verbose, -v
Prints each file name as it is processed.

--patternFile file, -p file
The pattern file to use. If this option is not given traceString first looks for .traceString in the current directory, then in $HOME and then wherever the executable lives.

--pipe
Use traceString as a pipe.

--indent, -i
If there is no traceString comment then add a new line and match current indentation. See INDENTATION Section for more details

--rtest, -r
Do NOT rename the generated files from *.new.* to the original name.

--name
Print the list of routines that would be converted while not actually doing the conversion.

--active file
file contains a list of ``fully qualified'' names of routine (typically gotten from --name). Only these routines will be instrumented. It is illegal to have both --active and --inactive at the same time.

--inactive file
file contains a list of ``fully qualified'' names of routine (typically gotten from --name). None of these routines will be instrumented. It is illegal to have both --active and --inactive at the same time.

--remove
Removes all trace strings from file.

--default name
Use name instead of default as the default pattern.

--debug, -d
Debug mode where every token is flushed to the output. If there are problems, use debug mode to make sure that all the output to the generated file has happened.

--printStates, -S
A developer only option used to track down parsing errors. It prints the changes in state that the FSM uses.

--version
Print the version and quit.


BUGS:

traceString does not add trace strings to routines internal to a class or struct. That is, in

  class X
  {
     X(int a) { ... }
  };

X::X(int a) will not be instrumented.

traceString can be fooled by the pre-processor. For example:

  int FORTRAN(foo)(int a) { ...}

is not understood by traceString. This can be converted to:

  #define f_foo FORTRAN(foo)
  int f_foo(int a) { ...}

The bookend comments /* %TRACE% */ must be in pairs. It removes the old code between the blocks and replaces it with new.


USAGE EXAMPLES:

Use the following to loop over directories: dir1, dir2 and file1.C

  traceString -v dir1 dir2 file1.C

traceString can be used as a pipe. This may be useful if part of a Makefile.

  traceString --pipe < file.C > tmp.C


AUTHOR

Robert McLay