47 Writing Hypersim Patterns
Model Builder User's Guide  /  VII Extending Simics  / 

48 Writing OS Awareness Software Trackers

48.1 Basic concepts of the software tracker

Writing a software tracker consists of creating two objects, a tracker and a mapper, plus adding a composition class.

The idea is that the tracker keeps track of the machine state, passes that data on to the tracker state in the OS Awareness framework, which then passes this on to the mapper that maps the tracker data into a node tree. The framework provides interfaces for users to read and get updates for the node tree. More about using OS Awareness can be found in Analyzer User's Guide. For a tracker writer the framework provides interfaces for reading and getting updates to the system, the machine state.

The directions of communication between different parts of OS Awareness is shown in figure 29.

Figure 29. Communications between tracker, mapper and framework. Boxes are part of the framework.

48.1.1 Framework benefits

The tracker state in the framework is used as a step in the communication between the tracker and the mapper. The concept of having both a tracker and a mapper that communicates via the framework gives some advantages:

  1. The framework will combine different updates from the tracker into as few updates as possible for the mapper:

  2. The framework will take care of storing and checkpointing the tracker state data.

  3. The output of one tracker could be represented in different ways using different mappers.

  4. The framework will help to provide error checks and logging.

See section 48.2.1.1 for information about the interface the tracker uses to add tracker state to the framework and section 48.3.2 for interfaces the mapper can use to retrieve tracker state.

A tracker could create a node tree directly, without using the tracker state or having a mapper. But this documentation will show the recommended way, which is having both a tracker and a mapper.

48.1.2 Node tree

The node tree is how the software tracker represents the tracked target software (or hardware) state. The mapper will start by creating one root node to which it can then add nodes to. The tree can have any width or depth of nodes. It is up to the mapper to represent the tracked data in a good way.

See section 48.3.3.1 for the interface used to create a node tree.

The node-tree command of the software object can be used to print the node tree.

See Analyzer User's Guide for more information about how a node tree looks and how the user can script against it.

48.1.3 Empty skeleton from a tracker

The easiest way to get started with writing a software tracker in C is to use the project-setup command with the --osa-tracker option in a project. This will add skeleton code for a tracker, a mapper and a composition class which can be used as a base to implement the new tracker from.

48.1.4 Linux tracker example

The source code includes an example of how to implement a tracker and mapper, sample-linux-tracker. The goal is to introduce the reader to important OS Awareness concepts and interfaces required to create a new tracker and mapper.

The example is a Linux tracker with limited functionality. The sample Linux tracker will work for single threaded programs running a single core version of qsp-x86 using the qsp-linux-common.simics target script.

To get this sample into a project run the following command:

project-setup --copy-module sample-linux-tracker

The sample Linux tracker will introduce the following concepts:

The Linux tracker example is not meant to explain how the Linux kernel stores it task structures and other data. For information about that, turn to the Linux kernel source code or some documentation about the kernel.

48.2 Writing a Tracker

The tracker is the object that tracks software or hardware by monitoring parts of the machine state (memory, registers, etc) and collecting data it is interested in. The tracker will provide the data it has collected to the framework for it to be mapped into a node tree by a mapper object.

48.2.1 Tracker and mapper communication

The tracker will pass on its state to the framework using the osa_tracker_state interface. See 48.1.1 for information about the tracker state in the framework.

The tracker and mapper will communicate with entities, which are a unique number that the tracker decides, this can for example be the address of a task struct (as for the Linux tracker) or something else that can be represented as a unique number. Associated with the entity there will be a dictionary of properties for that entity. The keys of the dictionary must be of string type. The mapper will then interpret each entity and represent their properties as nodes in the node tree.

48.2.1.1 osa_tracker_state_admin interface

This section will specify how to use this interface while writing a tracker, for details about each function and its arguments see the API reference manual.

48.2.2 Retrieving and monitoring the machine state

In order for a tracker to be able to read out the current state of the system and get notifications for changes of the target system state the OS Awareness framework provides two different interfaces.

48.2.2.1 osa_machine_query interface

This interface provides functions to get the current state for system. This includes reading the registers of a processor, reading memory and getting processor mode.

See the API documentation for details about what exact functions this interface provides and how to use them.

48.2.2.2 osa_machine_notification interface

This interface provides functions for getting callbacks when the state of the system changes. This can provide notification for when memory is accessed, a register is written, processor mode changes or an exception occurs.

Notifications are provided as callbacks, meaning that the tracker should provide a function that the parent will call once the state changes.

Every function in this interface will return an id which can be used to cancel the notification by providing this id to the cancel function. Remember to cancel all notifications once a a processor is removed or when the tracker is disabled.

See the API documentation for details about what functions this interface provides and how the callback functions should be implemented.

48.2.3 Interfaces that the tracker is required to implement

48.2.3.1 osa_tracker_control interface

This interface is called from the trackers parent to do such things as enabling or disabling the tracker and adding or removing processors to the tracker. The tracker needs to implement this interface and all its functions in order to work. How to use the functions is described below, more information about the functions and their arguments can be found in the API reference manual.

48.3 Writing a Mapper

A mapper object is used to map tracker data into a node tree. The node tree should represent the state of the tracked system. See 48.1.2 for more information about the node tree.

One tracker could potentially have different mappers that build up different node trees from the data provided.

48.3.1 Interfaces that the mapper should implement

This section describes the interfaces that need to be implemented by a mapper. For detailed information about the functions and their arguments see the API documentation for that specific interface.

48.3.1.1 osa_mapper_control interface

The mapper must implement the osa_mapper_control interface which provides functions that are called from the OS Awareness framework when enabling, disabling or when mapper state should be cleared. Below are descriptions of what each of these functions are meant to do when called.

48.3.1.2 osa_mapper_admin interface

This interface only provides one function; tracker_updated. This function is called from the OS Awareness framework when the tracker state has been updated for a tracker that the mapper is subscribing to.

The function will provide a changeset, providing data for what has been updated in the tracker state, and an initiator telling what processor initiated this change.

The mapper is supposed to map added, removed or modified entities to nodes in the node tree and update the node tree using the osa_node_tree_admin interface (48.3.3.1).

The mapper can also filter out unwanted changes, such as entities or properties that it does not care about. The mapper itself can also modify properties or add additional properties to suit what data it wants to provide the node tree with.

The changeset also provides events that the tracker has sent out. Events are not stored in the tracker state; they contain non-persistent data which the mapper can use to call event in the osa_node_tree_admin interface (48.3.3.1) to provide the node tree with an event. This can, for example, be a system call in a Linux tracker. The mapper could also choose to filter out an event or modify it before passing it on to the node tree.

For details on the tracker_updated function see the API reference manual.

48.3.1.3 osa_mapper_query interface

Both functions in this interface are optional, if not implemented the function should be set to nil in the interface.

48.3.2 Retrieving tracker state

The mapper is supposed to build up node trees from the tracker state. In order to do so the mapper must be able to get the current tracker state and be able to get updates once the tracker state is updated. The OS Awareness framework provides two interfaces for reading the current tracker state and for getting notifications when the tracker state is updated.

48.3.2.1 osa_tracker_state_query

This interface provides functions for getting tracker state from the OS Awareness framework. The functions it provides are the get_entity function, which allows getting the properties of a certain entity, and the get_entities which returns a dictionary with all the entities for a certain tracker. Neither of these functions are necessary for writing a working mapper, but the get_entities function is recommended to use during the enable phase to make sure that the mapper works as a guest to some other mapper.

48.3.2.2 osa_tracker_state_notification

This interface provides functions for subscribing to updates from a certain tracker. The subscribe_tracker function should be called during the enabling phase to tell the framework that this mapper wants to receive tracker state updates (through calls to the tracker_updated function in the osa_mapper_admin interface) for a certain tracker.

The tracker object to subscribe to is usually specified by having an attribute in the mapper.

The unsubscribe_tracker function is normally not used, this will tell the framework that the mapper no longer wants updated for the specified tracker. This is only useful if a mapper subscribes to several trackers and no longer wants updated for some of them, for example a stacked mapper were one of the guests is removed.

When the framework is disabled it will automatically remove any subscriptions so the next time it is enabled again the mapper will have to call subscribe_tracker again.

48.3.3 Creating a node tree

The purpose of a mapper is to map the tracker state into a node tree. The OS Awareness framework provides an interface for creating and modifying a node tree which the mapper should use.

48.3.3.1 osa_node_tree_admin interface

The osa_node_tree_admin interface is part of the OS Awareness framework and is the part used to create node trees. The mapper calls the functions of this interface to create and maintain a node tree.

All modifications to the node tree has to be inside a transaction, which is started with the begin function and ended by the end function of this interface. Transactions can be stacked as long as the order is so that a transaction started later is ended before any earlier started transaction. Notifications for the node tree changes will be sent out when all transactions have ended.

A mapper should start out by creating a root node using create once the mapper is enabled. This will give the mapper the root node id of its node tree. After that the mapper can add child nodes by using the add function. Nodes can be updated using the update or set_property functions, removed using the remove function and set as activated or deactivated using the activate and deactivate functions.

Removing a node will also remove all of its child nodes. So removing the root node will end up removing all nodes in the node tree.

The activate function marks a node as active for a certain processor for a node in the node tree. The deactivate function deactivates an active node. Only one node can be set as active per processor at once, so when setting a new active node there is no need to call deactivate on the previously active node as that will be deactivated when a new node is activated. For an active node all its ancestors will be seen as active on that processor in the node tree too.

A mapper can also do some formatting of outputted data for certain properties using the register_formatter function. This formatter will apply when get_formatted_properties in the osa_node_tree_query interface is called. One case were this can be used is when a number should be displayed in hex format.

48.3.3.2 Properties with special meaning

There are some node properties that have special meaning. These are not necessary, but might be needed for certain built-in functions and services to work.

Some services also requires a leaf node to only be active on one processor at a time.

48.4 Writing a Composition Class

In order for the tracker and mapper to work with the OS Awareness framework a tracker composition object should be added. This allows adding and removing a tracker to a system and implements so that the tracker and mapper will receive control signals from the framework.

48.4.1 Creating the composition class

The class will be written in python and it should inherit from the framework.tracker_composition class of the os-awareness module. So the composition source should import the following:

from simmod.os_awareness import framework

Then the composition class should be created as follows:

class empty_software_tracker_comp(framework.tracker_composition):

One current limitation in the system is that the composition class must always be named as the tracker class with a _comp suffix. In the example above this composition class would be for the tracker with class empty_software_tracker.

48.4.2 Methods that need to be implemented

It is also recommended that the composition class implements the info and status commands by adding the _info and _status methods.

48.4.3 Supporting Parameters

In order to be able to support parameters the composition class need to implement the osa_parameters interface. This will be done by adding the following line, followed by the implementation of the three interface functions:

class osa_parameters(pyobj.Interface):

The details for the osa_parameters interface can be found in the API documentation.

The OSA framework also provides a helper function, save_parameters_file, which can be used to save a parameters file. This function is only available from Python and located in simmod.os_awareness.framework. It takes four options, filename, tracker_cls, parameters, and overwrite. In case of failure, a FrameworkException exception will be raised.

It can be called like this:

framework.save_parameters_file(
    "linux.params", "sample_linux_tracker", {...}, False)

48.5 Checkpointing

Checkpointing for OS Awareness works a bit special. The enable state of the OS Awareness framework is not checkpointed, so the framework will always be disabled after a checkpoint is loaded. This means that if the framework is not enabled before starting the simulation, after loading a checkpoint, the state of the tracker, mapper and framework might be inconsistent with the target software. So if the simulation starts before enabling the framework will send out a clear_state call to all trackers and mappers. When receiving such a call the tracker or mapper should clear all its internal state so that it can be enabled from scratch again, without any state in the node tree or framework tracker state.

If on the other hand the framework is enabled directly after loading a checkpoint the tracker and mapper should just continue as before the checkpoint.

48.5.1 Support checkpointing in models

The following should be thought about in order for trackers and mappers to work with checkpointing.

48.5.2 Micro-checkpointing

Simics uses so called micro-checkpoints when executing in reverse. In order for a tracker or mapper to support reverse execution there are some things to think about:

  1. Other objects cannot be accessed when a micro-checkpoint sets an attribute, as those objects may not yet have been updated with the restored state. The order of which different objects are restored is not specified.
  2. The functions in the osa_machine_query cannot be called from an attribute setter when the simulation is restoring state. This will give an error.
  3. Notifications in the osa_machine_notification interface are not checkpointed, so the tracker will have to cancel notifications that are no longer valid and install new notifications for the restored state.
  4. As opposed to ordinary checkpoints, restoring a micro-checkpoint will be done while the tracker and mapper are already enabled. This could require special handling for some attributes.

In order to help models support reverse execution, the OS Awareness framework provides an interface, osa_micro_checkpoint. This interface has two functions:

47 Writing Hypersim Patterns