[Previous Example] [Next Example] [Overview of Examples] [User's Guide]

Examples\Advanced\Fault_Injection

Fault Injection with Aprobe

Introduction

This demonstrates how to use Aprobe for fault injection, which is the introduction of (intentional) errors into a program to test its error handling behavior.

This example applies the concepts introduced in several other examples, including Simple\Ex10_Umbrella_Probes, Advanced\Exception, and Advanced\Stub_Subprogram.

In this case, we're going to "inject a fault" into the fprintf() function. This will simulate its failure, and should force the calling program to report an error. We use the umbrella (i.e., nested probe) mechanism to ensure we don't break any other calls to fprintf(), especially those calls that are used to report the error itself.

Target Program

For this example, and several others, we use a single C++ class, named OutFile, which supports writing text to the screen or a file. This is defined in OutFile.h and implemented in OutFile.cpp.

The main program is in TestOutFile.cpp. It creates an OutFile object called oFile using the parameterless form of the constructor, which writes to standard output.

The Write operation is done by a local function called WriteLine(), which appends a newline and calls the OutFile::Write method, which in turn calls fprintf().

Writing the Probe

The APC file for this probe example is fprintfError.apc, and shown below:
// ------------------------------------------------------------------------
// fprintfError.apc 
//    A probe to stub fprintf() and return an error
//    ***only when called from WriteLine***
// ------------------------------------------------------------------------

// errno.h defines the error names
#include "errno.h"

probe thread 
{
   // log all calls to fprintf() here, for illustration purposes
   probe "fprintf()"
   {
      on_entry
        printf("> Calling fprintf()\n");
        ap_LogTraceback(99);
   }

   // disable fprintf() only when called from WriteLine
   probe "TestOutFile.cpp":"WriteLine"
   {
      probe "fprintf()"
      {
         on_entry 
         {
           printf("> Breaking fprintf()\n");
           log("Breaking fprintf()");
           ap_StubRoutine;
         }

         on_exit 
         { // Simulate no disk space left
           $errno = ENOSPC;
           $return = -1;
         }
      }  // end probe on fprintf()
   }  // end WriteLine "umbrella" probe

} // end probe thread

You need to compile fprintfError.apc into fprintfError.dll, and you must tell the APC compiler that TestOutFile.exe is the executable program from which to read the necessary debug information. This tells the APC compiler about the type of the global variable $errno (which is known, even though fprintf() itself has no debug info).

Running The Example

As always, the process is:
  1. Build the probe.
  2. Run the program with Aprobe.
  3. Format the collected data.

When you run the program (in Step 2), it calls WriteLine once with the command-line name and once again for each command-line argument. That first call will be intentionally "broken" within fprintf() by our probe, and this should cause an exception to be thrown. The catch block in main() then calls fprintf(), via OutFile::Message, to report the error, and this call to fprintf() works fine because it's not "under the umbrella" of WriteLine(). The output looks like this:

 
   > Calling fprintf() 
   > Breaking fprintf() 
   > Calling fprintf()
   TestOutFile: Write: <stdout>: No space left on device 
   > Calling fprintf()
    29 chars written before failure.

(Note that the time lag after writing the first "> Calling fprintf()" is the loading of the Windows StackWalk support.)

For purposes of illustration, we also called ap_LogTraceback in a separate probe on fprintf() so that we could see what happened "outside the umbrella" of WriteLine(). When you run apformat, you see (from the traceback) that the first call to fprintf() came through WriteLine() and the other calls didn't:

   Traceback:
   ---------------------------------------------------------
   extern:"fprintf()"
     ==> extern:"OutFile::Write(char const*)" at line 71 (OutFile.cpp)
     ==> "TestOutFile.cpp":"WriteLine()" at line 15 (TestOutFile.cpp)
     ==> extern:"main()" at line 24 (TestOutFile.cpp)
     ==> extern:"mainCRTStartup()" + 0x00d3
   ---------------------------------------------------------

Breaking fprintf()

   Traceback:
   ---------------------------------------------------------
   extern:"fprintf()"
     ==> extern:"OutFile::Message(char const*,int)" at line 85 (OutFile.cpp)
     ==> extern:"main()" at line 30 (TestOutFile.cpp)
     ==> extern:"mainCRTStartup()" + 0x00d3
   ---------------------------------------------------------

   Traceback:
   ---------------------------------------------------------
   extern:"fprintf()"
     ==> extern:"OutFile::Message(char const*,int)" at line 86 (OutFile.cpp)
     ==> extern:"main()" at line 30 (TestOutFile.cpp)
     ==> extern:"mainCRTStartup()" + 0x00d3
   ---------------------------------------------------------

Summary

From this example you have learned:
[Previous Example] [Next Example] [Overview of Examples] [User's Guide]