Simics instrumentation framework defines a set of new interfaces that could be
used to add instrumentation. Most of these are optional by the design. In
principle, the only interface that must be used for instrumentation to show up
in the list-instrumentation command are some Python functions in Simics’
instrumentation module.
Currently, the only supported interface for implementing instrumentation tools
is to use the instrumentation.make_tool_commands() Python function. This makes
it easier to write instrumentation tools, and to automatically implement a
number of standard commands for those. These commands will automatically
interface the instrumentation “database” which keeps track of currently used
instrumentation (tools, providers and filters objects connected).
This factory function should be called from the tool’s simics_start.py file,
which is automatically run when Simics starts. The
instrumentation.make_tool_commands() function takes many arguments (allowing
customization on which commands that should be created), but only a few
arguments are actually required.
A tool which makes use of the instrumentation.make_tool_commands() function,
must implement the instrumentation_tool interface. The
instrumentation_tool.connect() method should return an object pointer with the
associated data-structure for the connection. As shown in figure 36
and figure 37


By having a dedicated object for each connection, the tool will more easily be able to handle simultaneous calls from providers that execute in different threads in Simics. This requires that each connection maintains its own connection specific data (and not access any connection global data). The tool can then fetch the connection specific data from all connections and merge these into a unified view for the complete result of all connections.
If a connection is about to be removed, the instrumentation_tool.disconnect()
function is called. This requests the tool to un-register the connection from
the provider and to remove the associated connection object, as illustrated in
figure 39.

When a connection is created, the framework also creates an additional small
Simics object of the instrumentation_filter_aggregator class. This object is
what the filters communicate with, when enabling/disabling each connection. The
aggregator will look at all sources and control enabling and disabling with the
instrumentation_connection interface. The filter implements the
instrumentation_filter_master interface and talks to the aggregator via the
instrumentation_filter_slave interface. See figure 39.

Figure 40 shows how the aggregator takes input from multiple sources and decides if the connection should be enabled or disabled at this time. As long as any filter-source is disabled, the connection will be disabled.

Instrumentation setup is regarded as per session, that is, the connected instrumentation is not saved in any checkpoints. This means that the user will need to re-establish the instrumentation connections when starting from a checkpoint. All instrumentation tools and filters should set
class_data_t.kind = Sim_Class_Kind_Pseudo.
The API functions and interfaces that a tool should use are located in the
simics/simulator-iface/instrumentation-tool.h header file.
To get started writing an new instrumentation tool, a skeleton can be created by running the following command in the project:
$ cd my-project
my-project$ ./bin/project-setup --instrumentation-tool my_tool
This will create a new modules/my_tool directory containing associated
required files. Search for USER-TODO markers in the generated files for a
description on what, and where, additional logic needs to be inserted. It can
also be useful to take a look at the distributed source files of example tools,
that are included in the package.
Tools can be called simultaneously from providers in different executing threads. It is up to the tool to handle this parallelism itself. The tool has some choices how to handle multi-threading:
The interfaces needed to design a filter is located in the
simics/simulator-iface/instrumentation-filter.h header file.
To get started writing an new instrumentation filter, a skeleton can be created by running the following command in a project:
$ cd my-project
my-project$ ./bin/project-setup --instrumentation-filter my_filter
This will create a new modules/my_filter directory containing associated
required files. Search for USER-TODO markers in the generated files for a
description on what, and where, additional logic needs to be inserted. It can
also be useful to take a look at the distributed source files of example
filters, that are included in the package.
A filter must implement the instrumentation_filter_master interface and drive
the instrumentation_filter_slave interface for the slaves it has been
assigned. The slaves are instrumentation_filter_aggregator objects which are
automatically created when a connection is created.
When a filter object is created, it must first be assigned a session-unique
“source-id”. This can be retrieved from the Python
instrumentation.get_filter_source function. This can be achieved from the
filter’s simics_start.py file, which should contain a command for creating the
filter. Once the object has been created, the Python code could inform its
source-id to the filter by calling the
instrumentation_filter_master.set_source_id function.
The instrumentation_filter_master.add/remove_slave functions will be called
whenever <tool>.add-filter is adding this tool, or when additional connections
are added or removed from the tool.
Note that filters could be assigned slaves located in different cells (executing
in different threads). It is up to the filter to handle this, for example by not
supporting it, to avoid strange threading problems. This can be achieved by
returning false in the add_slave method when the provider_obj is located
in a different cell than previous slaves.
How a filter enables/disables the slaves is up to the filter. There are some scenarios:
Figure 41 show how a filter reacts on a provider and only enables/disables the slave which is associated with the same provider.

In figure 42, however, the filter tries to enable/disable connections
in two different cells. This could potentially crash. If cross-cell filtering is
needed, the connection in the other cell must be relayed through a
SIM_run_alone() function, which will be delayed and invoked when it is safe to
do cross-cell calls.

How the actual communication between a provider and tool should be achieved, must be designed case by case. The instrumentation framework itself, does not have any specific requirements on this. When a new provider interface needs to be designed, it is recommended to re-use design philosophies and naming schemes from already existing interfaces, keeping provider interfaces as consistent as possible.
In some way, the tool must inform the provider that it wants to get instrumentation information. Typically there are two designs for this:
Two generic interfaces can be implemented in the provider (when found useful):
instrumentation-order and instrumentation-move commands.list-instrumentation-callbacks command to list
callbacks that has been installed in the provider.These interfaces are located in the
simics/model-iface/instrumentation-provider.h header file.
Simics CPU instrumentation is provided on top of the generic instrumentation framework, where the processor model acts as a instrumentation provider. This chapter describes Simics’ processor instrumentation API.
A tool that wants to connect to the CPU instrumentation and use the
instrumentation.make_tool_commands function must set the
provider_requirements argument to cpu_instrumentation_subscribe, and maybe
more interfaces.
The cpu_instrumentation_subscribe interface is the main interface used, but
there are also other interfaces that must be used in certain circumstances as a
consequence of some of the callbacks. Figure 43 shows some of the
interfaces that are used.
Note, not all CPU-models support all parts of the CPU Instrumentation API.

A processor does not require any implemented interfaces from a tool, instead a tool will subscribe for information from a processor and register function callbacks which are invoked when the subscribed feature is triggered on a processor.
The interface can be used in two different ways:
NULL or None, in
C or Python respectively, as connection pointer. This allows the interface to
be used from a Python script, without the need to create Simics connection
objects. With this setup, the user must control each callback by itself. When
invoked, these callbacks will be called prior to the connection grouped
callbacks, which also allows the processor model itself to be extended using
the same interface.The interfaces used for the CPU instrumentation are located in the
simics/model-iface/cpu-instrumentation.h header file. The architecture
specific interfaces are located in simics/arch/x86-instrumentation.h, etc.
Subscribing to CPU instrumentation can be global (all instructions and/or memory accesses) or more carefully selected for each instruction at a time by the tool.
For example, assume a tool wants to monitor when certain instructions are executed. This can be done in different ways, each with pros and cons. The following list tries to highlight the workings of these different ways.
With global instrumentation By registering a
callback with the cpu_instrumentation_subscribe interface using the
register_instruction_before_cb() method, the tool will get a callback for
each instruction executed. If the tool is only looking for certain types of
instructions, it would need to use the cpu_instruction_query interface’s
get_instruction_bytes() method and filter out the unwanted instructions.
This will become very slow. See figure 44.

With selective instrumentation The tool can filter
out which instructions it wants to get a callback from more selectively. This
is done by registering a callback with the cpu_instrumentation_subscribe
interface using the register_cached_instruction_cb() method. The tool will
now be called when each instruction is about to be cached in the CPU model’s
internal instruction cache (this is a Simics internal cache and not the same
as the cache found on a real processor).
Just as before, the tools must decode what instruction that is
about to be executed using the cpu_instruction_query interface’s
get_instruction_bytes() method. For the interesting instructions
the tool can now register a dedicated callback using the
cpu_cached_instruction interface’s
register_instruction_before_cb() method. Each cached instruction
callback also carries a user-data pointer, which can be used by
the callback. This means that the analysis is performed only
once. There are, however, multiple reasons why this internal cache
may be flushed, if that happens the instruction must be
re-decoded.
Using the cpu_cached_instruction interface is much faster than filtering for
an instruction of interest each time it is executed, since the callback will
only be called for the requested instructions. See figure 45.

With a counter This is if the tool only wants to
count how many times certain instructions are executed. The
cpu_cached_instruction interface with the add_counter method, supports the
fastest way to count instructions. Adding a pointer to a 64-bit counter for
specific instruction means that the counter will increment each time this
particular instruction is executed. This has very low overhead. See figure
46.
