See Simics CMake Reference Manual for details on the Simics CMake package.
There are many reasons for using CMake instead of GNU Make. Some are opinionated while others are not. The major upsides comparing these two are:
CMake is a generator of build systems while GNU Make is just a build system. This allows CMake to split the build in two phases: configuration and build. And that allows CMake to apply more structure to the flow. It's also a lot faster as configuration only need to happen once.
CMake is not self-modifying, unlike GNU Make, making the flow easier to understand and debug. The flow is also more script-like with support for functions and macros making it easier to understand and develop.
Variables in functions are scoped so there is no leaking and contaminating of global variables. CMake also uses a property system where properties can be set for each individual target, leading to even less cross-target contamination and thus fewer bugs.
CMake has a better and more structured way of tracking target dependencies; making it easy to control and understand the build flow.
CMake consists of just a few basic build rules, making it easy to use. There
are only add_library, add_executable and add_custom_target for adding
things to build. The basic and common functionality of how to locate and
invoke the compiler is driven through properties and features, with sensible
defaults if not set. So for simple targets you don't have to write much code
while for complicated targets you can still write complicated logic to
satisfy the build requirements/dependencies.
CMake's language is supported by many of the most popular IDEs, providing both CLI and UI support. The script-like syntax and standardized split between code and data (e.g. the ConfigCache.txt that keeps all cached variables) makes it parseable by tools and IDEs. The built-in support to generate compile_commands.json allows IDEs to provide contextual C/C++ support.
CMake is cross-platform. Though you still have to handle OS-specific things, most of the basics is handled automatically or provided through standard mechanisms.
CMake supports and highly encurages out-of-source-tree builds. And because of this it also handles multiple coexisting build trees out-of-the-box, which is useful to separate debug and release builds for example.
CMake is a build system generator. It defines targets (modules, libraries, executables, custom) and their dependencies. The dependency scope can be private, transitive (interface) or both (public). This is all defined in CMakeLists.txt files starting from the root and adding more files as they are consumed by CMake.
The build system is generated into a build tree by invoking CMake. The path to the source tree root, the path to the build tree and the type of build system to generate (Ninja, GNU Make, etc) are the only required inputs. Additional configuration parameters such as the build type (Debug or Release) and where to locate the compiler etc can be provided from CLI or through some UI.
Once the build system has been generated, it can be run through the build
system runner (Ninja, GNU Make, etc) directly from the build tree "just like
normal" or through the CMake do-it-all binary (i.e. cmake --build <path to build tree> --target <target>).
The best way to understand how CMake works is to think of everything as
targets. A target is what you build, for example an executable or a library. It
probably depends on a few libraries and those are also targets. It might depend
on a header-only library and that is also a target. All targets are linked (not
necessarily by a linker, but conceptually) using target_link_libraries. The
scope, properties and features of each target defines how it will interact with
other targets.
Currently the minimum required version of CMake is 3.22.
The requirement comes from our use of ENVIRONMENT_MODIFICATION in simics_add_test() function.
The latest is the greatest, as later versions not only contains new features but also bugfixes and improved performance. Thus the recommended version is the latest version.
Installing CMake locally should be easy but if access to the host's package management system is not granted or it does not provide a recent enough version; installing CMake (and Ninja) in your local folder or home directory can be done swiftly using pip.
Running with at least 3.24 is preferred as it adds the --fresh command line
option that allows you to re-run the configuration without any of the cached
variables.
These are the known limitations:
These sources of reference will give you everything you need:
See Simics CMake Reference Manual for details on the Simics CMake package.
The Simics Base repo contains converted modules, e.g. the
sample-device-* modules are easy and straight forward and covers the most
common usage.
Links to the official documentation:
An Introduction to Modern CMake (tutorial):
Dos and don'ts:
$ <path to Simics Base>/bin/project-setup . --with-cmake
If a CMakeLists.txt file already exists, run with the --force flag.
CMake is not a build system in itself, it is a generator of build systems.
Thus the traditional GNU Make driven build process is, by design, split in two parts when working with CMake:
cmake --build <path to build tree>. Changes to configuration generally require the user to re-run the configuration phase, though some changes will automatically trigger a re-configuration.It's important to make this distinction. It allows CMake to split the collection of static properties from dynamic properties collected during each build. This allows the build to run much faster.
This is also the reason why CMake frowns upon globbing, and so this should be avoided if possible.
Simics expects to locate Simics modules in the "host lib" folder, e.g. <project>/linux64/lib, so this is where Simics CMake targets emits them. The intermediate files remains in the build directory configured by the user. Unlike the old GNU Make driven flow a CMake driven flow can have multiple build directories which could be useful to separate debug and release builds for example.
NOTE: This is the recommended approach
The simplest option (after getting used to it) is probably to use the cmake
program from CLI. On Linux with bash-completions enabled you can tab-complete
the options (but not the targets) and it contains a lot more than just the
--build option making it a very useful tool to master.
This is how to bootstrap (i.e. generate a build system) and build everything:
$ cmake -S . -B bt -G Ninja -DCMAKE_BUILD_TYPE=Release
$ cmake --build bt
Where:
-S . ; points to where the top-level CMakeLists.txt is located (i.e. the project)-B bt ; points to where the build tree should be created. Can be anywhere.-G Ninja ; select which generator to use (Ninja is the fastest)-D<flag>=<value> ; sets build flag. The CMAKE_BUILD_TYPE is mandatory.See cmake --build bt --help for more details. Here is a list of useful options:
--target X Y Z ; builds only X Y and Z targets--verbose ; adds verbosity, shows what you build--clean-first ; removes artifacts before building, useful to force a rebuildThere is also the ccmake program, which is an ncurses frontend to configuring
the build directory:
$ ccmake bt
NOTE: This is not the recommended approach, only listed for reference
During the initial transition from GNU Make to CMake, before getting acustomed
to the cmake tool, it might feel more convenient to continue with a
make-driven flow:
$ make
$ make <target>
NOTE: this depends on the following files and local changes:
cmake-wrapper.mk file has been copied to the project
(as is currently done by project-setup)
-include cmake-wrapper.mk
The GNU Make wrapping, provided by cmake-wrapper.mk, handles the following:
D=1 enables a debug build and this is done in the build-debug folderD=0 enabled a release build and this is done in the build-release folderCC, CXX, CMAKE and NINJA and
reconfigure the build on forced rebuilds.LIMITATIONS:
clean-, clobber-, test-, etc does not
work.cmake directly as shown in the previous section.Not covered by this documentation. It's recommended to run through the cmake --build indirection.
Not covered by this documentation.
The Simics CMake package adds support for running tests via CTest and enables this support by default.
To run all available tests:
$ ctest --test-dir bt
Useful parameters to control how the tests are run:
-j N ; run N tests in parallel, will speed up the testing significantly-R <regexp> ; only run tests matching regexp-E <regexp> ; exclude tests matching regexp-L <regexp> ; only run tests with labels matching regexpSimics tests are added to CTest via the simics_add_test() function. See Simics CMake Reference Manual for details.
CMake expects the root of the source directory (passed to cmake) to contain a
top-level CMakeLists.txt file that defines the CMake project. This file sets
up compiler settings (but not the compiler!) and defines what libraries and
executables to build. Typically the configuration for these libraries and
executables are located in separate CMakeLists.txt files added to the project
by calls to add_subdirectory().
The Simics CMake package provides the simics_add_module() function, and some
other helpful functions described below, to help the user define what to build
in the project. See Simics CMake Reference Manual for details and basic usage of all functions.
In order to use the Simics CMake package it must be added to the project configuration:
find_package(Simics REQUIRED)
This requires that CMAKE_PREFIX_PATH has been set to ${SIMICS_BASE}/cmake where SIMICS_BASE is the absolute path to the Simics Base installation. The Simics project, when created with the --with-cmake flag, comes with a default CMakeLists.txt file that provides this path explicitly:
find_package(Simics REQUIRED CONFIG NO_DEFAULT_PATH PATHS ${SIMICS_BASE}/cmake)
The default top-level CMakeLists.txt file generated by project-setup invokes the
simics_find_and_add_modules() function which:
modules subdir
to the CMake projectmodules subdir.LIMITATIONS:
simics_find_and_add_modules really only compares directory names, so if a
local copy exists the corresponding directory in the add-on package will not
be loaded regardless of its content.EXCLUDE_FROM_ALL so they don't pollute the 'all' target. They can still be
built by specifying the target(s) explicitly.NOTE: CMake will take care of providing the paths of the added modules to the compiler so it is no longer a Simics project requirement that modules must be copied into the project in order to build them.
As a user you are not required to use the top-level CMakeLists.txt provided by
project-setup. Nor are you required to call simics_find_and_add_modules, as
the basic functionality is provided by the Simics CMake package. In fact, you
are not even required to create a Simics project. See simics_project_setup()
in the Reference Manual
for a detailed description of the supported modes. Using CMake provides a lot
more flexibility over the old GNU Make driven Simics module build system.
To build all Simics modules registered with CMake, build the simics-modules
target. To list all modules, build the list-simics-modules target.
Conversion follows these three steps.
Run the gmake-to-cmake converter to get a good starting point. For
example:
$ ./bin/gmake-to-cmake modules/AT24Cxxx
Make note of any warnings or errors shown during conversion, for example
WARNING: MODULE_CFLAGS used, please review:
Make adjustments as necessary by comparing Makefile and CMakeLists.txt side-by-side
Please note that the converter is not meant to handle all types of input, and it only detects and reports a small set of problems. It should be used as a starting point only, as writing a tool that understands GNU Make is out-of-scope for the Simics CMake project.
For trivial modules, such as the sample-device-* modules, the converter works and can be trusted. But for more complicated modules that use GNU Make logic, conditional code, generates files, expands and filters variables to construct new lists, etc etc; the user must conduct a manual review.
For shared common code and other folders that does not define a Simics module it's probably easier to start from scratch with an empty CMakeLists.txt or use some existing common code as template.
The following sub-sections labeled A..F provides details and examples of how to solve some of the common problems with constructing a CMakeLists.txt file for Simics.
Most of the time it's better to expand these indirections and use explicit
names for classes and source files. Where indirections are warranted CMake does
support variables via set(...) function. CMake has an extensive library of
utility functions that operates on variables and lists to handle the most
common problems.
wildcard to locate filesMost of the time it's better to explicitly list all the files so CMake can
track their dependencies properly. CMake does have support for path pattern matching via
file(GLOB ...) function but these pattern matches are only run during configuration
phase and not between consecutive builds; which means you have to explicitly
reconfigure the project if new modules are added to the CMake project. To
mitigate this, CMake's file(GLOB ...) has a CONFIGURE_DEPENDS option that
causes the pattern match to be re-evaluated on every build. There is a cost involved of
course, so use with caution and avoid if possible.
EXTRA_MODULE_VPATHPlease note that this section is about common code. See (D) below for referencing files from other modules.
There are two distinct ways common code is used by modules, and they need different solutions.
In this case, it's always better to let the other module build a static library
and add a dependency on that library target. This is done using
add_library(...) and target_link_libraries(...):
In module A (the user):
target_link_libraries(A PRIVATE B)
In module B (the provider):
add_library(B STATIC 1.c 2.c 3.c ...)
The conversion of module A is handled by the converter, but the conversion of
module B has to be done manually. In order to build module B it's likely that
include paths must be added explicitly, and this is done by
target_include_directories(...). Paths to Simics standard includes are
otherwise added to module A via the simics_add_module(...) function, but B
cannot depend on Simics::Simics as it's a STATIC. Instead we add a dependency
on Simics::includes.
In module B:
target_include_directories(B
INCLUDE .)
target_link_libraries(B
PRIVATE Simics::includes)
In the snippet above keywords PRIVATE and INCLUDE are used to control the scope
and transitivity of these configurations. PRIVATE means it only applies to the
current target. INCLUDE means is only applies to targets that depend on the current
target. Configuration that should apply to both the current target and targets that
depend on the current target, must use the PUBLIC keyword.
The use of . in target_include_directories is expanded within B to the current
source path of B, but added to the include paths of A.
In this case, unlike (C1), the referenced source files (if any) must be built
by module A to honor the module specific defines. To achieve this in CMake we
define an INTERFACE library instead of a STATIC library. INTERFACE
libraries do not produce any output; they are used to pass values and can be
used as target dependencies:
In module A:
target_link_libraries(A PRIVATE B)
add_compile_definitions(DEVICE_NAME=A)
In module B:
add_library(B INTERFACE)
target_sources(B INTERFACE 1.c 2.c 3.c ...)
target_include_directories(B INTERFACE .)
Here INTERFACE means module A (the user) and the sources listed by module B
(the provider) are added to module A. The obj files produced are put into
module A's build directory and will not be re-used by any other module that
also depends on module B. Please note that since the files are built by A and A
gets Simics include paths added by simics_add_module there is no need to
explicitly depend on Simics::includes here; unlike in (C1).
An INTERFACE type library does not have to provide any sources, it can just
provide include directories. This is useful in (D2) below. Specifically for DML
modules common code is typically shared this way:
add_library(cmn-common INTERFACE)
target_include_directories(cmn-common INTERFACE .)
add_library(cmn-common::imports ALIAS cmn-common)
EXTRA_MODULE_VPATHThere are two type of files: source files and header files.
To share source files between modules a STATIC type library as described in
(C1), or an INTERFACE type library as described in (C2), must be created and
given a unique name. By convention the NAME given to simics_add_module is the
module name and cannot be re-used:
In module A
target_link_libraries(A PRIVATE B::shared)
In module B
add_library(B-shared STATIC event-queue.c)
target_link_libraries(B-shared PRIVATE Simics::includes)
add_library(B::shared ALIAS B-shared)
or
add_library(B-shared INTERFACE)
target_include_directories(B-shared INTERFACE .)
target_sources(B-shared INTERFACE foo.c)
add_library(B::shared ALIAS B-shared)
'B-shared' can be any name not already present in the CMake configuration. It is recommended to provide an alias to clearly indicate that 'shared' is a target in the B module.
The simics_add_module function automatically provides an INTERFACE type
library, as described in C2, in addition to the MODULE type library; adding the
current module directory as target include directory. The following three
aliases can be used for this INTERFACE type library:
<MODULE_NAME>::includes, <MODULE_NAME>::headers and
<MODULE_NAME>::imports. They all work the same and differ only by name, to
provide some syntactic sugar matching the language used by the Simics module.
In module A:
target_link_libraries(A PRIVATE B::includes)
In module B: no changes needed as the INTERFACE type library is auto-generated.
It might be tempting to use the 'MODULE_NAME' directly in
target_include_directories, but this does not work. Simics modules must be
fully isolated entities without runtime dependencies and thus are created with
MODULE library type. This prevents CMake from linking a Simics module to
anything else.
This is a common pattern in Simics, for example module_load.py which is
generated from common code fetched from some other module. To solve this
problem, use the CMake standard add_custom_command with built-in cat
functionality:
add_custom_command(
OUTPUT module_load.py
COMMAND ${CMAKE_COMMAND} -E cat a.py b.py > ${CMAKE_CURRENT_BINARY_DIR}/module_load.py
DEPENDS a.py b.py
)
Also make sure that generated files are part of SOURCES. Generated
files should be output in the current binary directory,
simics_add_module searches first the current source directory and
then the current binary directory for Python and DML files.
See sample-risc/CMakeLists.txt for an example of this.
Sometimes a module needs to re-use python files from other modules as part of its own simmod structure. This can be done using standard CMake functions, but since it's fairly common a convenience method has been provided:
simics_copy_python_files(ICH10 FROM ICH FILES ich_commands.py ich_updaters.py)
See simics_copy_python_files() documentation in the Reference
Manual for more
details on usage.
The CMake+Ninja combo is the best/fastest for CLI based development. More powerful IDEs might leverage CMake differently. See CMake documentation for details. Here are a couple of tips to get started.
cmake --help contains everything you need. Especially --help-command to
learn more about each command. The bash shell supports tab-completion
out-of-the-box so it's easy to navigate. Of course, there is also the
cmake.org website.
The cmake -E utility provides portable ways to do many file operations such as cat and should be used over if-conditional code:
COMMAND ${CMAKE_COMMAND} -E cat a.py b.py > ${CMAKE_CURRENT_BINARY_DIR}/module_load.py
See cmake -E --help or the online documentation for more details.
For example, and as the documentation also states, use the
target_include_directories instead of include_directories etc. The
target_-prefixed versions of their counterpart require one of the
INTERFACE, PUBLIC or PRIVATE keywords to define the scope of the command:
PRIVATE - only applies to the scope of the targetINTERFACE - applies to the scope of whoever uses/depends on the targetPUBLIC - applies to both Use target properties and avoid globals.
In the GNU Make driven Simics build system all flags added to MODULE_CFLAGS
was passed to the compilation step and all flags added to MODULE_LDFLAGS was
passed to the linking step. The CMake API provides functions at a finer
granularity for expressing these things:
target_include_directories()target_compile_definitions()target_compile_options()target_link_directories()target_link_libraries()target_link_options()The converter does not try to be clever and solve this problem; it just warns
about it. For clarity it is important that flags are passed using a combination
of these function calls. Please note that the user must also classify the scope
of the flags, i.e. PRIVATE, INTERFACE or PUBLIC.
CMake has defined a set of log-levels that should be used to differentiate messages. The most important ones are:
FATAL_ERROR ; CMake Error, stop processing and generation.WARNING ; CMake Warning, continue processing.NOTICE ; Important message printed to stderr to attract user's attention.STATUS ; The main interesting messages that project users might be interested in.VERBOSE ; Detailed informational messages intended for project users.DEBUG ; Detailed informational messages intended for developers working on the project itself as opposed to users who just want to build it.TRACE ; Fine-grained messages with very low-level implementation details. Messages using this log level would normally only be temporary and would expect to be removed before releasing the project.The log-level to use can quickly be changed by passing --log-level to cmake.
See cmake.org:message() for more details and more log levels.
Correct use of message log-levels can improve debuggability, but should that not be enough there is always the sledge hammer!
$ cmake --trace ...
This emits a lot of details to stdout about what cmake is doing when
processing the CMakeLists.txt files. To limit the output to only a few files of
interest, add the --trace-source option.
See cmake.org:--trace for more details.
CMake has built-in utility functions for printf-debugging:
cmake_print_properties()cmake_print_variables()See cmake.org:CMakePrintHelpers for details.
In general, and hence by default, you want to build shared libraries as they
are easier to share among your build targets. But in Simics, this breaks module
isolation and must be avoided. So remember to pass STATIC when building helper
libraries, i.e.: add_library(<target> STATIC ... )
By default the Simics CMake configuration will set
CMAKE_POSITION_INDEPENDENT_CODE to ON which enables all STATIC library
builds to pass the -fPIC flag to the compiler. If this is not the case, it
can be selected with target_compile_options(<target> PRIVATE -fPIC)
See D2 (for modules) or C2 (for common code) for more details.
Note that shared/imported .dml files counts as headers in this case.
See D1 (for modules) or C1 (for common code) for more details.
In the GNU Make driven build system, code sharing was done by adding the
other.c file to the SRC_FILES variable in the Makefile of the module where
it was going to be used. Causing the same files to be built over and over
multiple times. Though this is still possible to do by explicitly providing the
absolute path to the file, it is not the recommended approach in CMake.
E.g. instead of doing this:
target_sources(versatile-devices
PRIVATE ${SIMICS_PROJECT_DIR}/src/extensions/keycodes-common/keycodes.c)
you should be doing this:
target_link_libraries(versatile-devices
PRIVATE keycodes-common ...)
where keycodes-common defines a STATIC library like this:
add_library(keycodes-common STATIC keycodes.c)
target_include_directories(keycodes-common PUBLIC . ...)
Please note that special flags and defines set by the target where this static
library is used, do not propagate into the build of the static library. Such
flags must also be set on the static library or the source files should be
compiled as part of the "user" target as described by (C2). For example, to
build for SIMICS_API=6 one must pass target_compile_definitions(<target> SIMICS_6_API).
The Simics CMake package provides functions and targets to build Simics modules. The Simics modules are meant to be dynamically loaded by Simics and as such can rely on Simics to have loaded all the libraries a module depends on, e.g. vtutils and python. External dependencies should be avoided, to allow the module to be relocatable to other hosts.
Other binaries built by the same project, such as utilities and unit tests, might
require RPATH being set though and this can be done on-demand by each target
by setting the BUILD_RPATH property:
set_target_properties(generate-dml-from-xml
PROPERTIES BUILD_RPATH $ORIGIN/libs:${SIMICS_LIB}:${SIMICS_SYS_LIB})
Passing explicit linker options also works, but should be avoided:
target_link_options(${MODULE_NAME} PRIVATE -Wl,-rpath,${SIMICS_SYS_LIB})
Projects that build mostly other things can setup RPATH globally in the
top-level CMakeLists.txt using the CMAKE_BUILD_RPATH cache variable. See
official CMake documentation for more details.
Sometimes a set of flags only apply to a subset, or just one, of the files that
make up a target. Setting the flags on the target might then be suboptimal as
it would affect everything built within that target. Here one could use
set_source_files_properties to alter properties per source file.
For example, if a compilation unit does not compile with -O3 the optimization
can be reduced per unit.
Another example is, if _FORTIFY_SOURCE=2 has been enabled then that requires
__OPTIMIZE__ > 0 ; so if the current optimization level is to be reduced to
zero one must also undefine _FORTIFY_SOURCE:
set_source_files_properties(zuc.c
PROPERTIES COMPILE_OPTIONS "-O0;-U_FORTIFY_SOURCE")
To configure a build tree for generating GCOV coverage the following two conditions must be met:
include(${SIMICS_BASE_DIR}/cmake/coverage.cmake)USE_COVERAGE=1 and provide LCOV, GCOV and
GENHTML if binaries are not already in PATH. See below for details.The coverage.cmake adds the following targets:
init-coverage ; to initialize the coverage collectionreset-coverage ; to reset the coverage counters and start overcapture-coverage ; to capture new/more coverage after running testsgenerate-coverage-report ; generate an HTML reportfilter-coverage ; internal targetgenerate-coverage-report-internal ; internal targetEnabling USE_COVERAGE=1 will enable the following flags for coverage:
add_compile_options(--coverage -g -Og)
add_link_options(--coverage -Wl,--dynamic-list-data)
Then a normal build will produce the GCOV instrumented binaries and a normal run (or sequence of runs) of some tests/work-loads will generate the aggregated coverage data.
Once coverage data has been generated, a coverage report can be generated via
the generate-coverage-report utility target. The HTML report is generated in <build-tree>/coverage/index.html
See example below for details and step-by-step on all of this.
The targets are enabled only if LCOV binary is found in PATH or if the path
to LCOV is provided in the LCOV cache variable.
Assumptions and cache variables used by coverage.cmake:
PATH, then set LCOV cache variable:PATH, then set GCOV cache variable. GCOV is typically found next to your GCC compiler and thus automatically found.PATH, then set GENHTML cache variable. GENHTML is typically found next to the lcov binary in LCOV package and thus automatically found. $ cmake -S . -B btc -G Ninja -DCMAKE_BUILD_TYPE=Debug -DUSE_COVERAGE=1 -DLCOV=/usr/itm/lcov/1.16/bin/lcov
$ cmake --build btc
$ cmake --build btc --target init-coverage
$ ctest --test-dir btc -j40
$ cmake --build btc --target generate-coverage-report
$ cmake --build btc --target generate-coverage-report-internal
Inspect the report in btc/coverage/index.html
To clear/reset the coverage counters between reports:
$ cmake --build btc --target reset-coverage
Sanitizers are a great way to find additional bugs during run-time. A typical flow would be to compile targets used in tests with ASAN and UBSAN, to get tests with a higher bug coverage. More information about what type of bugs ASAN and UBSAN can reveal can be found at https://github.com/google/sanitizers. Keep in mind that sanitizers are not supported on Windows.
To add ASAN/UBSAN conditional compilation for a Simics module, you need to call
the simics_add_sanitizers function with your module name as the parameter.
Example:
simics_add_module(my-device
CLASSES my_device
SOURCES my-device.dml
SIMICS_API 6
)
simics_add_sanitizers(my-device)
This will mark the my-device module as a module that should be compiled with
sanitizers when sanitizers are enabled. To enable compilation with sanitizers,
you should set the following CMake variables accordingly. Note that you can
enable each option individually or all at once.
| Variable | Enables |
|---|---|
| USE_UBSAN | UndefinedBehaviorSanitizer |
| USE_ASAN | AddressSanitizer |
| ASAN_STACK_USE_AFTER_RETURN | Stack-use-after-return (enables USE_ASAN) |
| LSAN_SUPPRESSION_FILE | A suppression file for LSAN for false positive memory leaks |
| LSAN_MALLOC_CONTEXT_SIZE | LSAN Malloc context size |
These cache variables can be set during CMake configuration like so:
$ cmake --build btc -DUSE_ASAN=1 -DUSE_UBSAN=1 -DASAN_STACK_USE_AFTER_RETURN=1
or enabled after configuration by using ccmake or similar CMake configuration
tool. The tests added with simics_add_test() would then have to be executed with
ctest for the sanitization to apply.
Keep in mind that these variables will have an affect for all CMake build types (Debug, Release etc).
ASAN, on average, adds a 2x slowdown. It also adds a RAM overhead, along with longer compilation times. It therefore makes sense to use ASAN when verifying the model's correctness, such as running tests. It might not make as much sense to use ASAN compiled models for the purpose of verifying software that interfaces with the model.
It also makes sense to use high level of compilation optimization when compiling sanitized modules, since that minimizes the slowdown. Compiling with debug information is not mandatory for ASAN to trigger, but is needed to get useful information in the stack trace.
ASAN_STACK_USE_AFTER_RETURN adds even more performance and memory overhead,
but as the name suggests can find use of pointers pointing to stack allocated
object after a function return. See https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn for more information.
If you want to interface with your Simics modules compiled with sanitizers using
the Simics CLI, you can run the CMake target simics-asan from within your
CMake build directory. Example:
$ cmake --build bt --target simics-asan
This makes sure LD_PRELOAD and the ASAN options are properly setup before
launching Simics, which is a requirement for the modules to trigger on errors
correctly.
Using simics_add_dead_methods_check() on a target will add a post build step
that will check if the module contains any dead DML methods. This will only
happen if the USE_DML_DEAD_METHODS_CHECK option is enabled during
configuration.
By default, the check will only apply to source code that belongs to the current
module. However, the simics_add_dead_methods_check() has an argument
EXTRA_SOURCES that expands the scope of the dead methods analysis. This
argument can be used to scan common code.
Dead DML methods are methods that have been implemented but are never called.
One example would be an implemented post_init() method in an attribute, but the
attribute never instantiates the post_init template. This would result in a
post_init method that is never invoked.