Legal Notices

Chapter 1:
  Quick Start

Chapter 2:
  Introduction and Installation

Chapter 3:
  About Memory Analysis

Chapter 4:
  Finding Memory Leaks

Chapter 5:
  Finding Memory Errors

Chapter 6:
  Startup Options

Chapter 7:
  Viewing Error Messages

Chapter 8:
  Viewing Source Code

Chapter 9:
  Tips and Techniques

Chapter 10:

Chapter 11:
  Obtaining Support

Chapter 9:  Tips and Techniques

Daemons and other background programs
Optimization Issues
Performance Issues

Daemons and other background programs

Normally starting a program under ZeroFault is quite simple: just insert the zf command in front of the normal command line for the program.  However, using ZeroFault with daemon programs (e.g. those started by init or inetd) and other programs that are not started from the shell requires using one of the following methods:
  • One method is to use the -e flag to cause ZeroFault to debug any programs that are loaded using the exec system call. This allows you to run the main program under ZeroFault, and also debug any children that the main process executes.

  • The other method is to create a shell script wrapper that invokes ZeroFault on the target process. The first step is to rename the real program:
      $ mv myprog myprog.real
    Then create the shell script that takes its place and calls ZeroFault on the original program:
      $ cat > myprog <<\EOF
      exec zf myprog.real "$@"
      $ chmod +x myprog
    Of course you may choose to insert arguments to ZeroFault after the zf command. Now, whenever myprog gets executed it actually executes ZeroFault on the real myprog program.

Optimization Issues

When the optimizer is turned on in the compiler, it sometimes places load instructions ahead of the conditions that they are logically dependent on. For instance in the following code fragment the optimizer loads the value that s points to before it checks to see if the pointer is NULL, even though the code explicitly says to check the pointer first:

    foo(char *s)

        if (s) {
            while (*s) {
                if (*s == '\t')
                    *s = '\n';
This is the relevant assembler code generated by the optimizer:
    0| 000000 ai.      34630000   2     LR_R     gr3,cr0=gr3
    5| 000004 lbz      88030000   1     L1Z      gr0=(*)uchar(gr3,0)
    5| 000008 cmpi     2C800000   2     C4       cr1=gr0,0
    4| 00000C bcr      4D820020   0     BT       CL.6,cr0,0x4/eq ,taken=50%
The ai instruction tests the value of the s pointer, the lbz loads the byte pointed to by s, the cmpi tests to see if the loaded byte is zero, and finally the bcr branches to the end of the function if the pointer was NULL.

If this function is called with a NULL pointer, then ZeroFault would normally generate an RNULL error. However, the optimizer only generates code like this if it knows that nothing destructive will result from it. For this reason, ZeroFault doesn't generate USTKR or RNULL errors in modules that have been optimized, to avoid generating false error messages.

The only automatic way that ZeroFault can infer that a module has been optimized is if it doesn't contain line number debug information, such as that generated by the -g compiler option. Therefore it is suggested that you always use the -g flag without the -O flag, and vice-versa. The -g flag makes debug output much more useful, but it does use more memory, especially when running under ZeroFault (see below).

Performance Issues

Programs typically run slower under ZeroFault than natively. The following steps may improve performance:
  • Only compile with debugging information (the -g compiler flag) those modules that are important for debugging. The symbol information that the compiler generates can take a lot of memory, especially in C++ modules. Similarly, always optimize code that will not figure prominently in your debugging efforts. It may be much easier to rebuild specific object modules without optimization than to suffer the performance impact of running large amounts of unoptimized code.
  • Only have one instantiation of ZeroFault running at a time on a given system. Since the emulator may use a substantial amount of paging space and CPU, having multiple instances running at the same time could degrade system performance. This particularly comes into play when a process forks many children, each of which becomes a separate emulated process. Note that having multiple user interfaces running can consume many resources, so running zf with the -d none flag and then running zf_ui after the process exits can reduce the overall resource usage.

© Copyright 2013 The ZeroFault Group, LLC. All rights reserved. All logos and trademarks are property of their respective owners.