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.
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().
// ------------------------------------------------------------------------
// 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).
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
---------------------------------------------------------