C-RUN is an add-on runtime analysis tool completely integrated into IAR Embedded Workbench. It helps you to ensure code quality in your C and C++ code by inserting efficient instrumentation code into the application for automatic runtime error checking capabilities.
This guide contains a walkthrough for evaluating the runtime error checking capabilities offered by C-RUN.
You can evaluate C-RUN right now free of charge with a 14-day time-limited trial version of IAR Embedded Workbench for Arm or IAR Embedded Workbench for Renesas RX. Register and download using the corresponding links provided in the Software Requirements table, under Required License Types, for the target architecture of your choosing.
This guide works for any of these products, given that the requirements are met:
Product | Target architecture | Required version | Required license types |
---|---|---|---|
IAR Embedded Workbench | Arm | 8.11 or later | - Cortex-M, or - Standard, or - Extended, or - Time-limited trial version |
IAR Embedded Workbench | Renesas RX | 5.10 or later | - Standard, or - Time-limited trial version |
Note
No special action needs to be taken when you evaluate C-RUN in code-size-limited mode using the time-limited trial version. This special mode was created to allow customers to evaluate C-RUN using smaller pieces of code while exploring its tight integration with the IAR C-SPY Debugger.
Tip
For access to a non-limited license contact your IAR representative.
A number of example projects, created as demonstrations of the feature set offered by C-RUN, can be found in this repository.
To run the examples:
- Clone this repository, or download the latest release.
- Launch the IAR Embedded Workbench IDE for a supported target architecture.
- Open the
Workspace.eww
file, found in the folder for the desired target architecture.
The workspace will launch with a pre-selected project. In the workspace Overview there are tabs for switching from one project to another:
Tip
The arrow buttons scroll to tabs that did not fit into the window width. The window can be resized. Alternatively, right-click a project in the Overview and choose Set as active from the context menu.
Below you will find specific instructions for running each of these projects.
The first example explores the most straightforward functionality in C-RUN: the Arithmetic checks.
To run this example, do this:
-
Select the Arithmetic project in the workspace window.
-
Choose Project → Options (Alt+F7) → Runtime Checking.
-
Make sure that C-RUN Runtime Checking is enabled
- Make sure all these checks are selected:
-
Click
OK
to close the Project Options dialog box. -
Choose Project → Download and Debug (Ctrl+D) to start executing the application.
-
The C-SPY Debugger will hit a breakpoint at
main()
. Press F5 to resume the program execution. -
The execution will Stop, with the following line of code highlighted:
In the editor window, C-SPY highlights in green the statement in which the execution stopped when the runtime error checking was triggered. In addition, C-RUN narrows the problem down and highlights the root cause of the runtime error.
Concurrently, the C-RUN Messages window (View → C-RUN → Messages) will break down the detected runtime error as well as its call stack. Clicking on the call stack takes you to the corresponding source code line.
- Examine the remaining detected errors by pressing F5 to resume the program execution until the next error message, or until the execution ends.
-
Stop the debug session (Ctrl+Shift+D).
-
When you create applications, there are situations where relying on the wrap-around property of overflown unsigned integers is efficient. In such cases, specific C-RUN checks can simply be deselected. In the project's Runtime checking options, disable these:
- Rebuild, Download and Debug (Ctrl+D) the application, and examine the changes in the error detection capabilities.
Note
- Disabling the checks you do not need will generally make execution faster while decreasing the code size.
- The Default Action for C-RUN is to Stop at each detected error. In the C-RUN Messages window, the Default action can be switched to Log or to Ignore.
- C-RUN Messages can also be filtered by rules. For details, refer to the Creating rules for messages section in the C-SPY Debugging Guide.
The second example explores the Bounds Checking capabilities provided by C-RUN.
To run this example, do this:
-
Select the Bounds-checking project.
-
Choose Project → Download and Debug (Ctrl+D) to start executing the application.
-
After the execution reaches the breakpoint in the
main()
function, resume the execution (F5).
Note how the application finishes executing (
exit()
) without any apparent errors of any kind.
-
Stop the debug session (Ctrl+Shift+D).
-
Choose Project → Options (Alt+F7) → Runtime Checking and enable bounds checking:
- Rebuild and run the project.
Note that now C-RUN highlighted an out-of-bounds access for
*(ap+2)
. However, the execution stopped earlier, at the firstprintf()
statement. The reason is that the compiler determines that pointer addresses used as parameters for thoseprintf()
calls are related and, with that, C-RUN efficiently can test them in one go.
- Press F5 to resume the execution and inspect the remaining C-RUN Message:
Tip
It will stop at the last statement of the application, indicating that the assignment is performed out of bounds. From a bounds-checking perspective, dynamically allocated memory is no different from local pointers, static buffers, or buffers on the stack.
- Again, choose Project → Options (Alt+F7) → Runtime Checking and take a look at the Global bounds table group box.
Pointers that can be accessed through other pointers must have information in a table stored in memory. The Number of entries field allows you to fine-tune the number of slots available in the table.
Warning
Leaving Number of entries blank will remove the limit, resulting in a large global bounds table (4k slots!). The number of entries you need is often fairly low, so you might want to experiment with shrinking the global table, to lower the resource consumption for the instrumentation. Do not worry if you use a number that is too low; if this happens, you will get a warning message telling you that the global bounds table is running out of slots.
-
Set the Number of entries to
1
and close the Project Options dialog box. -
Rebuild, and Download and Debug (Ctrl+D) the application.
In this example, there is only one pointer that needs to be kept in the table, so a single entry is enough.
In scenarios where the main project relies on pre-built (third-party) libraries, bounds checking requires extra consideration for shared pointers.
The project Bounds-checking+libs is a modified version of the Bounds-checking project. The functionality available from the IntMax.c
file was moved to a Library project that produced a static library named MaxLib.a
, pre-built with no C-RUN bounds-checking instrumentation, typical for third-party libraries where the code cannot be changed.
When you use pointers and pointer arguments between the instrumented application code and the non-instrumented library code, you must inform the compiler (and linker) that functions in the library code do not have bounds-checking information. The best way to do so is to use the #pragma default_no_bounds
directive when including a library header, to inform the compiler that the library was not built with C-RUN Bounds-checking information:
To run this example, do this:
- Select the Bounds-checking+libs project.
This project comes with two build configurations, with different options for C-RUN:
DoNotCheckPointersFromNonInstrumentedCode
andCheckPointersFromNonInstrumentedCode
- Begin with the
DoNotcheckPointersFromNonInstrumentedCode
build configuration.
At the top of the Workspace window, you can switch between existing build configurations.
- Choose Project → Options (Alt+F7) → Runtime Checking and confirm these settings:
- Close the Project Options dialog box, and choose Project → Download and Debug (Ctrl+D) to start executing the application.
You should get no C-RUN errors, or any other indications that something is wrong. In the
DoNotCheckPointersFromNonInstrumentedCode
build configuration, turning off bounds-checking information for library headers worked well- (As it should do in most cases; returned pointers will not be bounds-checked but will have associated bounds that are always "large enough" to accommodate them.) In other words, this means that pointers originating from your code will be checked, but not pointers from the library. This should normally be perfectly acceptable and the process is non-intrusive in terms of code changes.
-
Stop the debug session (Ctrl+Shift+D).
-
Switch to the
CheckPointersFromNonInstrumentedCode
build configuration.
The
CheckPointersFromNonInstrumentedCode
build configuration demonstrates a situation where it is desirable to have bounds checking for pointers and returned pointers defined in the library code.
- Choose Project → Options (Alt+F7) → Runtime Checking and make sure that the C-RUN Runtime Checking options are:
- Build and run the application again. This time you should see one C-RUN message for the third
printf()
statement.
Read the source code and compare the use of
__as_make_bounds()
for the pointerap
to how the bounds are set for the pointer used in the nextprintf()
statement. We have also defined a project-specific macro to control the use of__as_make_bounds()
. This built-in function is used when the returned pointer must be given sensible bounds. If no bounds are given for returned pointers, a bounds error will be generated on the first access made to the pointer. Note that this build configuration defines a preprocessor symbol calledCONFIG
that conditionally compiles with__as_make_bounds()
calls to give pointers sensible bounds.
- Comment out one of the calls to the
CRUN_MAKE_BOUNDS()
macro, rebuild and run.
❔ Did it change the output in the C-RUN Messages window?
C-RUN can check for errors in how heap memory is being used. Heap checking can catch when the application tries to use already freed memory, non-matching deallocation attempts, and leaked heap blocks.
When you use heap checking, each memory block is expanded with bookkeeping information and a buffer area, so that the blocks as seen by the application are not located side-by-side.
The various checker functions examine the bookkeeping information and the buffer areas and report violations of correct heap usage.
Warning
- Using heap checking makes it much easier to find heap usage errors, but it is not fail-safe.
- Heap checking and bounds checking can complement each other in identifying dynamic memory usage errors. However, because of the potential impact in terms of performance and overhead, you are advised not to enable both at the same time.
To run this example, do this:
-
Select the Heap project.
-
Choose Project → Options (Alt+F7) → Runtime Checking.
-
Make sure that Use checked heap is enabled:
-
Choose Project → Download and Debug (Ctrl+D) to start executing the application.
-
Examine the source code comments for each reported error.
- Stop the debug session (Ctrl+Shift+D).
Note
The function HeapFunc3()
in Heap.c
can be enabled by uncommenting the CRUN_FULL_EDITION
macro definition. The function requires a full edition of C-RUN since its resource consumption will exceed the code size limitation in the time-limited trial version. Contact us in case you need the full license or a demonstration.
There are scenarios in which might not be possible to debug an application built with C-RUN information directly from the IDE. Below you will find some examples on how to use C-RUN in non-interactive mode.
The IAR C-SPY Command Line Utility (cspybat
) can run applications directly from the command line. This utility is suitable for non-interactive debugging and can be used in conjunction with IAR C-RUN where runtime analysis is desired. One typical scenario for considering cspybat
is within automated tests from a continuous integration environment. In this example, we will use the Heap
project.
- Close the IDE, launch a Command Prompt shell and change to the
Heap
project's folder.
cd \path\to\crun-evaluation-guide\arm
Tip
You will notice that, in this project's settings/
folder, the IDE automatically generated four files for each project(/build configuration):
File | Description | Example |
---|---|---|
<project>.<cfg>.cspy.bat |
Batch script for executing the application using cspybat |
Heap.Debug.cspy.bat |
<project>.<cfg>.cspy.ps1 |
PowerShell script for executing the application using cspybat |
Heap.Debug.cspy.ps1 |
<project>.<cfg>.general.xcl |
Extended command line (*.xcl ) containing general options |
Heap.Debug.general.xcl |
<project>.<cfg>.driver.xcl |
Extended command line (*.xcl ) containing backend/driver options |
Heap.Debug.driver.xcl |
Inspect the scripts and its accompanying files and you will see all project parameters that are needed to execute the application using cspybat
.
- Use the option
--rtc_enable
to enable C-RUN incspybat
.
echo --rtc_enable >> settings\Heap.Debug.general.xcl
- Execute the batch script.
settings\Heap.Debug.cspy.bat
Expected output example (click to expand)
C:\crun-evaluation-guide\arm>settings\Heap.Debug.cspy.bat C:\crun-evaluation-guide\arm\settings>"C:\IAR\EW\ARM\9.50.2\common\bin\cspybat" -f "C:\crun-evaluation-guide\arm\settings\Heap.Debug.general.xcl" --backend -f "C:\crun-evaluation-guide\arm\settings\Heap.Debug.driver.xcl" IAR C-SPY Command Line Utility V9.3.2.12345 Copyright 2000-2024 IAR Systems AB. Heap usage error @ 0x227a Core: 0 The address 0x20001249 does not appear to be the start of a heap block. Call Stack: HeapFunc1 in "C:\crun-evaluation-guide\common\Heap.c", 32:3 - 32:10 main in "C:\crun-evaluation-guide\common\Heap.c", 101:3 - 101:13 [_call_main + 0xd] Heap usage error @ 0x2280 Core: 0 The address 0x20001208 does not appear to be the start of a heap block. Call Stack: HeapFunc1 in "C:\crun-evaluation-guide\common\Heap.c", 34:3 - 34:10 main in "C:\crun-evaluation-guide\common\Heap.c", 101:3 - 101:13 [_call_main + 0xd] Heap usage error @ 0x2286 Core: 0 The address 0x20001249 does not appear to be the start of a heap block. Call Stack: HeapFunc1 in "C:\crun-evaluation-guide\common\Heap.c", 35:1 - 35:1 main in "C:\crun-evaluation-guide\common\Heap.c", 101:3 - 101:13 [_call_main + 0xd] Out of heap space @ 0x2292 Core: 0 There is no more heap space to allocate. A request for 4120 bytes could not be satisfied. A total of 36 bytes have been allocated in 1 heap blocks. Call Stack: HeapFunc2 in "C:\crun-evaluation-guide\common\Heap.c", 44:3 - 44:9 main in "C:\crun-evaluation-guide\common\Heap.c", 102:3 - 102:13 [_call_main + 0xd] CSpyBat terminating.
Note
- All
cspybat
options for C-RUN begin with--rtc_*
. For more information about these options, see cspybat options for C-RUN in the C-SPY Debugging Guide. - The IDE might regenerate the project's
*.xcl
files, overwriting your changes. Useattrib +r settings/*.xcl
if you wish to pin it and avoid further automatic changes performed by the IDE. Useattrib -r settings/*.xcl
to remove the read-only attribute.
Now consider scenarios where there is a need for electrical insulation, or for running the device in its real operating environment where there should be no access to debug ports, etc. In this section you will find information on how to customize your applications to reroute C-RUN raw messages to the desired communications channel so that they can be captured during the field testing for post-mortem analysis.
Note
In this guide, we will not offer any source code that relies on hardware-specific communication peripherals. It is assumed that the Hardware Abstraction Layer (HAL) in the application has been correctly initialized and it is ready for operation, providing serial transmission support through a function such as serial_send(channel, unsigned char)
. Ultimately serial_send()
can be replaced by an equivalent function for writing C-RUN raw messages to any suitable media (e.g., SPI, I2C, RAM, Flash, etc.).
-
Add the
$EW_DIR$/<target>/src/lib/crun/ReportCheckFailedStdout.c
source file to your project. -
Open
ReportCheckFailedStdout.c
.
On the editor window tab you might see [Read-Only] (or [RO]). This indicates that the file was added directly from the IAR Embedded Workbench installation directory, which is write-protected. Leave this file unchanged for future reference.
-
Choose File → Save As.
-
A message is displayed asking "The file is read-only. Would you like to remove the read-only attribute?". Click No, navigate to the project's folder and save a copy of the file there.
Now, [Read-Only] (or [RO]) should not be visible on the editor tab.
- In the function
__iar_ReportCheckFailedStdout()
, comment out the call to__write()
and insert the following code:
//__write(_LLIO_STDOUT, (unsigned char const *)buf, (size_t)(b - buf));
for (size_t i = 0; i < (size_t)(b - buf); i++)
{
serial_send(USART7, (unsigned char)buf[i]);
}
- Choose Project → Options → Linker → Extra Options and use this command line option:
--redirect __iar_ReportCheckFailed=__iar_ReportCheckFailedStdout
Note
- Alternatively, the low-level I/O
__write()
function can be overridden, provided that it does not affect other parts of your application. For more information, refer to the article Overriding and redirecting library modules without rebuilding the entire library.
- Connect the Debug probe to the target.
- In the IDE, choose Project → Download → Download Active Application to flash the device with the the application.
- Power off and disconnect the debug probe from the board.
- Launch a Virtual Terminal Emulator (such as Tera Term VT) and connect it to the target board's serial port.
- Power up the board and verify that the C-RUN raw messages are being displayed in your Virtual Terminal Emulator.
- Save the collected
CRUN_ERROR
messages to a text file (e.g.,field-tests-with-crun.txt
).
In possession of the C-RUN raw messages collected from the field, feed it back to C-RUN itself using cspybat
. The cspybat
utility provides the --rtc_raw_to_txt
option for converting the raw messages to human-readable text.
-
Append the
--rtc_raw_to_txt="/path/to/field-tests-with-crun.txt"
to the project'ssettings/<project>.<cfg>.general.xcl
. -
Run
cspybat
:
settings/<project>.<cfg>.cspy.bat
Output example (click to expand)
C:\project\settings>"C:\IAR\EW\ARM\9.50.2\common\bin\cspybat" -f "C:\project\settings\project.Debug.general.xcl" --backend -f "C:\project\settings\project.Debug.driver.xcl" IAR C-SPY Command Line Utility V9.3.2.391 Copyright 2000-2023 IAR Systems AB. crun_fail Signed integer overflow -- Result is greater than the largest representable number: -- 1 (0x1) + 2147483647 (0x7fffffff). crun_fail Integer conversion failure -- Conversion changes the value from -2147483648 (0x80000000) -- to 2147483648 (0x80000000).
Note
Call Stack Information is not provided in this mode.
C-RUN is a robust runtime analysis add-on for IAR Embedded Workbench, designed to significantly improve your field testing. Interested in integrating C-RUN into your projects? Request a quote to find the licensing option that best fits your needs.
For comprehensive details on C-RUN, please refer to the C-RUN runtime error checking section in the C-SPY Debugging Guide included with your IAR product.
Follow us
on GitHub to get updates about tutorials like this and more.
For technical support contact IAR Customer Support.
For questions or suggestions related to this tutorial: try the wiki or check earlier issues. If those don't help, create a new issue with detailed information.