Main Content

Generate Standalone C/C++ Code That Detects and Reports Run-Time Errors

During development, before you generate C/C++ code, it is a best practice to test the generated code by running the MEX version of your algorithm. However, some errors occur only on the target hardware. To detect these errors, generate standalone libraries and executables that detect and report run-time errors, such as out-of-bounds array indexing.

By default, run-time error detection is disabled for standalone libraries and executables. To enable run-time error detection and reporting for standalone libraries and executables:

  • At the command line, use the code configuration property RuntimeChecks.

    cfg = coder.config('lib'); % or 'dll' or 'exe'
    cfg.RuntimeChecks = true;
    codegen -config cfg myfunction
  • In the MATLAB® Coder™ app, in the project settings dialog box, on the Debugging pane, select the Generate run-time error checks check box.

Run-time error detection can affect the performance of the generated code. If performance is a consideration for your application, do not generate production code with run-time error detection enabled.

Generated C Code vs. Generated C++ Code

If your target language is C, the generated code uses fprintf to write error messages to stderr. Then, the code uses abort to terminate the application. If fprintf and abort are not available, you must provide them. The abort function abruptly terminates the program. If your system supports signals, you can catch the abort signal (SIGABRT) so that you can control the program termination.

If your target language is C++, the generated code throws std::runtime_error exceptions for the run-time errors. When you call the generated C++ entry-point functions, you can catch and handle these exceptions by using a try-catch block in your external C++ code.

However, for run-time error checks inside parallel regions (either parfor loops or automatically parallelized for loops), the generated C++ code does not throw an exception. In such situations, the generated code uses fprintf to write error messages to stderr, and then uses abort to terminate the application. To learn more about automatic parallelization, see Automatically Parallelize for Loops in Generated Code.

Example: Compare Generated C and C++ Code That Include Run-Time Checks

In this example, you compare the run-time behavior of generated C and C++ code for a MATLAB® function that calculates the square root of its input argument. The generated code can accept only nonnegative real values and produces a run-time error for negative inputs:

  • The generated C code uses fprintf to write the error message to stderr. Then, the code uses abort to terminate the application.

  • The generated C++ code throws a std::runtime_error exception for this run-time error. In the C++ main function that you write to call the generated function, you catch and handle this exception by using a try-catch block.

Define MATLAB Function

Define the MATLAB function errorCheckExample in a separate file. This function calculates the square root of its input argument:

type errorCheckExample
function y = errorCheckExample(x)
y = sqrt(x);
end

Generate C Library and Executable

Generate a dynamically linked C library for errorCheckExample that accepts a double scalar input. Use a code configuration object with the RuntimeChecks parameter set to true. Also, use the -d option to name the code generation folder as codegen_c_dll.

cfg = coder.config('dll');
cfg.RuntimeChecks = true;
codegen -config cfg errorCheckExample -args 1 -d codegan_c_dll -report
Code generation successful: To view the report, open('codegan_c_dll/html/report.mldatx')

Open the code generation report and inspect the file errorCheckExample.c. The C function generated for your MATLAB function has the signature double errorCheckExample(double x). To calculate the square root, errorCheckExample invokes the sqrt library function which calculates only real square roots. So, errorCheckExample can accept only positive inputs. For negative inputs, errorCheckExample calls the generated utility function rtErrorWithMessageID that uses fprintf to write an error message to stderr and then uses abort to terminate the application.

static void rtErrorWithMessageID(const int b, const char *c,
                                 const char *aFcnName, int aLineNum)
{
  fprintf(stderr,
          "Domain error. To compute complex results from real x, use "
          "\'%.*s(complex(x))\'.",
          b, c);
  fprintf(stderr, "\n");
  fprintf(stderr, "Error in %s (line %d)", aFcnName, aLineNum);
  fprintf(stderr, "\n");
  fflush(stderr);
  abort();
}

When generating library code, the code generator also produces example main files main.h and main.c in the examples subfolder of the build folder. The supporting C files main_runtime_check.h and main_runtime_check.c are modified versions of these example files. The modified main function invokes errorCheckExample(-4), which produces a run-time error.

Run these commands to generate a C executable using the modified main files. Name the code generation folder codegen_c_exe. Name the executable file errorCheckExample_c by using the -o option with the codegen command.

cfg = coder.config('exe');
cfg.RuntimeChecks = true;
cfg.CustomSource = 'main_runtime_check.c';
cfg.CustomInclude = pwd;

codegen -config cfg main_runtime_check.c main_runtime_check.h errorCheckExample -args 1 -o errorCheckExample_c -d codegen_c_exe
Code generation successful.

Run the generated executable. Observe that it prints the error message that is hard-coded in the utility function rtErrorWithMessageID.

if isunix
    system('./errorCheckExample_c');
elseif ispc
    system('errorCheckExample_c.exe');
else
    disp('Platform is not supported');
end
Domain error. To compute complex results from real x, use 'sqrt(complex(x))'.
Error in sqrt (line 13)
./errorCheckExample_c: Aborted

Generate C++ Library and Executable

Generate a dynamically linked C++ library for errorCheckExample that accepts a scalar double input. Use a code configuration object with the RuntimeChecks parameter set to true. Also, use the -d option to name the code generation folder as codegen_cpp_dll.

cfg = coder.config('dll');
cfg.RuntimeChecks = true;
codegen -config cfg -lang:c++ errorCheckExample -args 1 -d codegen_cpp_dll -report
Code generation successful: To view the report, open('codegen_cpp_dll/html/report.mldatx')

Open the code generation report and inspect the file errorCheckExample.cpp. Similar to the C function generated in the previous section, errorCheckExample can accept only positive inputs. For negative inputs, errorCheckExample calls the utility function rtErrorWithMessageID. But in this case, the utility function throws a std:runtime_error exception that you can catch and handle in your hand-written main function.

static void rtErrorWithMessageID(const char *b, const char *aFcnName,
                                 int aLineNum)
{
  std::stringstream outStream;
  ((outStream << "Domain error. To compute complex results from real x, use \'")
   << b)
      << "(complex(x))\'.";
  outStream << "\n";
  ((((outStream << "Error in ") << aFcnName) << " (line ") << aLineNum) << ")";
  throw std::runtime_error(outStream.str());
}

When generating library code, the code generator also produces example main files main.h and main.c in the examples subfolder of the build folder. The supporting C++ files main_runtime_check.hpp and main_runtime_check.cpp are modified versions of these example files. The modified main() function invokes errorCheckExample(-4) inside a try-catch block. The block catches the exception and prints a modified message by prepending the string "Caught excaption: " to the message that the caught exception contains.

Run these commands to generate a C++ executable using the modified main files. Name the code generation folder codegen_cpp_exe. Name the executable file errorCheckExample_cpp.

cfg = coder.config('exe');
cfg.RuntimeChecks = true;
cfg.CustomSource = 'main_runtime_check.cpp';
cfg.CustomInclude = pwd;

codegen -config cfg -lang:c++ main_runtime_check.cpp main_runtime_check.hpp errorCheckExample -args 1 -o errorCheckExample_cpp -d codegen_cpp_exe
Code generation successful.

Run the generated executable. Observe that it prints the modified error message.

if isunix
    system('./errorCheckExample_cpp');
elseif ispc
    system('errorCheckExample_cpp.exe');
else
    disp('Platform is not supported');
end
Caught exception: Domain error. To compute complex results from real x, use 'sqrt(complex(x))'.
Error in sqrt (line 13)

Limitations

Run-time error detection and reporting in standalone code has these limitations:

  • Error messages are in English only.

  • Some error checks require double-precision support. Therefore, the hardware on which the generated code runs must support double-precision operations.

  • If the program terminates, the error detection and reporting software does not display the run-time stack. To inspect the stack, attach a debugger.

  • If the generated C code terminates, the error detection and reporting software does not release resources, such as allocated memory. The generated C++ code does not have this limitation. If the generated C++ code terminates, allocated memory and other resources are released.

  • In standalone code, the function error displays a message that indicates that an error occurred. To see the actual message specified by error, you must generate and run a MEX function.

  • In standalone code, if called with more than one argument, the function assert does not report an error and does not terminate execution. If called with a single argument, for example, assert(cond), if cond is not a constant true value, reports an error and terminates execution.

See Also

| |

Related Topics