cpu_instruction_query cpu_memory_query
API Reference Manual  /  5 Model-to-Simulator Interfaces  / 

cpu_instrumentation_subscribe

Description
The cpu_instrumentation_subscribe interface is implemented by processor classes that support CPU instrumentation. The interface can be used to monitor and instrument various actions through callbacks. It is designed for being more efficient than haps and will not synchronize any threads. This means that any user of the interface must assume that registered callbacks can be called in parallel by multiple threads if any of the multi-threaded execution modes are used in Simics.

A processor implementing the interface is regarded as a provider of instrumentation and is typically used by an instrumentation tool object. The tool object is supposed to register callbacks in this interface and act when they are called. To handle different threads it is recommended that the tools uses a connection object (a sub-object created by the tool) that registers these callbacks for each processor that it monitors. Then, any data collected can be stored in these connection objects and will thus not be subject to concurrent access from different threads that hosts the processors. The data can than be aggregated by the tool when appropriate. This approach needs no synchronization locks and allows for efficient simulation.

See instrumentation_order interface for more details on the callback order.

The cpu argument in all methods below is the processor object implementing this interface.

The connection argument is the "user" object of this interface, typically a connection object as described above. However, it can be any object and even NULL if there is no suitable object to pass. For instance, if the callback is registered by a Python script. In this case synchronization will be handled by the Python layer.

All registration methods in the interface install at least one callback, the cb argument, that will be called at a particular instrumentation point in the processor object. The Simics Cell Context API is available for use in these callbacks. The callbacks have different signatures depending on their use case. The data argument is user defined data that can be associated with each callback. Every time the callback is called this data will be passed as an argument to the callback. The data can be unique for each registration, even though the same callback pointer is used.

Note: Note, not all CPU models implement all parts of the instrumentation API. Register methods not implemented in this interface will be NULL.
SIM_INTERFACE(cpu_instrumentation_subscribe) {
        /* Callback specific methods */
        void (*remove_callback)(
                conf_object_t *NOTNULL cpu,
                cpu_cb_handle_t *handle);
        void (*enable_callback)(
                conf_object_t *NOTNULL cpu,
                cpu_cb_handle_t *handle);
        void (*disable_callback)(
                conf_object_t *NOTNULL cpu,
                cpu_cb_handle_t *handle);

        /* Callback groups methods, operating on several callbacks 
           associated to a connection. */
        void (*remove_connection_callbacks)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *NOTNULL connection);
        void (*enable_connection_callbacks)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *NOTNULL connection);
        void (*disable_connection_callbacks)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *NOTNULL connection);

        /* Subscribe methods */
        cpu_cb_handle_t *(*register_instruction_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_instruction_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_instruction_after_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_instruction_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_read_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_access_scope_t scope,
                cpu_memory_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_read_after_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_access_scope_t scope,
                cpu_memory_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_write_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_access_scope_t scope,
                cpu_memory_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_write_after_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_access_scope_t scope,
                cpu_memory_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_address_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_address_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_cached_instruction_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_cached_instruction_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_instruction_decoder_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_instruction_decoder_cb_t cb,
                cpu_instruction_disassemble_cb_t disass_cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_exception_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                int exception_number,
                cpu_exception_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_exception_after_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                int exception_number,
                cpu_exception_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_exception_return_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_exception_return_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_exception_return_after_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_exception_return_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_mode_change_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                cpu_mode_change_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_control_register_read_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                int register_number,
                cpu_control_register_read_cb_t cb,
                lang_void *data);
        cpu_cb_handle_t *(*register_control_register_write_before_cb)(
                conf_object_t *NOTNULL cpu,
                conf_object_t *connection,
                int register_number,
                cpu_control_register_write_cb_t cb,
                lang_void *data);
};
        
#define CPU_INSTRUMENTATION_SUBSCRIBE_INTERFACE \
        "cpu_instrumentation_subscribe"

Callback Related Methods

Every function in this interface that registers a callback returns a unique handle of type cpu_cb_handle_t. The remove_callback method removes the callback associated with the handle. The enable_callback and disable_callback methods can be used to temporarily enable and disable a callback.

The remove_connection_callbacks removes all callbacks associated with a connection object, i.e., all the callbacks that was registered with the same connection object. The enable_connection_callbacks and disable_connection_callbacks enables and disables all callbacks associated with a connection object. NULL cannot be passed to these methods to handle callbacks installed without any connection object.

The design philosophy is that registering and removing a callback can be relatively expensive, whereas enable and disable a callback should be cheap. On the other hand, a disabled callback can slow down the simulation speed a bit more compared to running without callbacks.

Instruction Related Methods

The method register_instruction_before_cb installs a callback that is called before an instruction is executed. The callback type is as follows:

typedef void (*cpu_instruction_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        instruction_handle_t *handle,
        lang_void *user_data);

The object obj is the connection or the user object that registers the callback (or NULL if there is no object) and cpu is the processor object executing the instruction. If a dedicated connection object associated with each processor is used, the object's private data can store the interface pointers needed to access processor state. This is a useful trick to speed up the simulation speed. Otherwise such interface pointers need to be acquired each time the callback is called. If no connection object is used the pointers can be saved in the callback's user data. The handle can be used to query more data about the instruction, using the cpu_instruction_query interface. The user_data is the user data associated with the callback.

The registered callback is called for every instruction type. Use the register_cached_instruction_cb method to control which type of instructions that should be instrumented.

register_instruction_after_cb installs a callback that is called after an instruction is executed. The callback is of the same type as for the before variant and is called for every instruction type. Use the register_cached_instruction_cb method to control which type of instructions that should be instrumented. Reading the program counter register for a control flow instruction in this callback will reflect the new location, whereas using the cpu_instruction_query for reading out the instruction address will still return the address of the control flow instruction.

None: When an exception occurs the instruction is aborted and any installed callbacks by the register_instruction_after_cb method are not called.


Memory Related Methods

register_read_before_cb installs a callback that is called before a memory read operation is performed. The callback type is as follows:

typedef void (*cpu_memory_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        memory_handle_t *handle,
        lang_void *user_data);

As for instructions, obj is the object that registered the callback and cpu is the processor doing the access. The handle can be used to further operate on the access by using the cpu_memory_query interface. The interface can for instance be used to read the data of the memory operation. For more information, see the documentation for the cpu_memory_query interface.

The scope argument defines the access scope for the callback. There are two defined scopes: CPU_Access_Scope_Explicit and CPU_Access_Scope_Implicit. The explicit scope installs a callback for every operation done explicitly by an instruction, such as loading or storing a value. The implicit scope installs a callback for every implicit access, such as table walks or memory accesses performed by exception and interrupt handling. If all accesses are requested the same callback function can be registered for both scopes by registering the callback twice, one for each scope.

If a memory access crosses a page boundary the access will be split into two separate calls, one for the first part covering the first page, and one for the second part covering the second page. See the get_page_crossing_info method in the cpu_memory_query interface for a way to distinguish the different cases.

The user_data is the data associated with the callback.

Similar to register_read_before_cb, the following three methods work in the same way:

register_read_after_cb installs a callback that is called after a memory read operation is performed.

register_write_before_cb installs a callback that is called before a memory write operation is performed.

register_write_after_cb installs a callback that is called after a memory write operation is performed.

Memory accesses of prefetch types or any other control operation calls the read callbacks. To distinguish them from reads an architecture specific interface can be used to lookup the access type. See the x86_memory_query interface for example.

None: When an exception occurs the instruction is aborted and any installed memory callbacks after this point are not called.


Addresses

Note: Only available on x86 targets.
register_address_before_cb can be used to register a callback that will be called each time a logical address is generated by an instruction for an explicit read or a write operation. This occurs before the actual memory operation takes place. This allows a user to inspect and change the target address for the operation. The callback has the following signature:

typedef logical_address_t (*cpu_address_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        logical_address_t address,
        address_handle_t *handle,
        lang_void *user_data);

The argument obj is the object installing the callback and the cpu is the processor generating the logical address. The handle can be used to extract more information about the address by using an architecture specific interface. See the x86_address_query for details.

The new logical address should be returned by the callback.


Cached Instructions

register_cached_instruction_cb is used for installing a callback that is called every time Simics inserts the instruction into an internal instruction cache, i.e., executing from a specific address with a specific execution mode for the first time. Executing the instruction again will typically not invoke the callback since the instruction is already placed in the instruction cache. However, if the cache is flushed and the instruction is executed again the callback will once again be called. The callback has the following signature:

typedef void (*cpu_cached_instruction_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        cached_instruction_handle_t *ci_handle,
        instruction_handle_t *iq_handle,
        lang_void *user_data);

From this callback one can use the cpu_cached_instruction interface and ci_handle to register instrumentation callbacks for this specific instruction alone. The installed callbacks will be called every time the instruction is executed (even the first time just after the cpu_cached_instruction_cb_t callback has returned). This means that the user can filter out certain instructions of interest and instrument only those. The iq_handle and the cpu_instruction_query interface can be use to do the filtering by examining the instruction. The user_data is the callback user data for the callback.

For callbacks registered for memory operations only those in the explicit scope issued by the instruction will be instrumented, e.g., hardware table walks and exceptions will not be considered by this method. To instrument these operations use the register_(read/write)_(before/after)_cb with the implicit scope instead.

If no callbacks are registered by calling the cpu_cached_instruction interface, the instruction will not be instrumented.


Instruction Set Augmentation

register_instruction_decoder_cb lets the user redefine instruction semantics in Simics, or implement new instructions. The cb argument is a callback function that will be called every time Simics decodes an instruction. From this callback the user can accept the instruction or deny it. In most cases this only happens once per instruction address since Simics usually caches decoding results in the internal instruction cache. If the cache is flushed the callback may be called again.

The callback signature looks like this:

typedef int (*cpu_instruction_decoder_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        decoder_handle_t *decoder_handle,
        instruction_handle_t *iq_handle,
        lang_void *user_data);

The instruction bytes are read by using the get_instruction_bytes method of the cpu_instruction_query interface together with the iq_handle. The returned value is of a cpu_bytes_t type. To access the bytes use the data and the size members in the returned value.

If the decoder requires more bytes (i.e., because the new instruction is longer), a negative integer value should be returned by the cb function, indicating the number of bytes needed. For example, if the available bytes are 3 but the decoder need at least 4 bytes, -4 should be returned. The callback will then be called again with more available bytes (this can be repeated if the new instruction requires even more bytes at this point). Note that requesting more data than actual needed can cause spurious page faults if the data crosses a page boundary.

If the instruction is accepted by the callback a positive integer number should be returned corresponding to the length of the instruction. In this case the register_emulation_cb method of the cpu_instruction_decoder interface should be called to set the actual (user) function that Simics will call each time the instruction is executed.

If the cb callback should ignore the instruction the number 0 should be returned. This means that any other registered decoder will have a chance to decode the instruction. If no decoder accepts it, Simics' default implementation will be used.

The register_emulation_cb method also needs the decoder_handle which is available in the dedoder callback. For more information, see the documentation of the cpu_instruction_decoder interface.

A disass_cb argument should also be passed to the register_instruction_decoder_cb method. This function is called every time Simics wants to disassemble an instruction. For every accepted instruction a corresponding disassembly string should be returned by this function. It has the following signature:

typedef tuple_int_string_t (*cpu_instruction_disassemble_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        generic_address_t addr,
        cpu_bytes_t bytes);

obj is the object registering the register_instruction_decoder_cb and cpu is the processor disassembling the instruction. addr is the address of the instruction in a generic form. This means that it is typically a physical address or a logical address depending on the context of the disassembling. The address can be used for offset calculations, i.e., displaying an absolute address instead of a relative one, for example in a branch instruction. The bytes argument should be used to read instruction bytes. The return value is of type tuple_int_string_t and should be filled with the instruction length and an allocated (e.g., malloc) string representing the disassembly of the instruction. The ownership of the string is transferred to the calling environment which will free it when it is no longer needed.

If too few bytes are passed for the instruction to be disassembled a negative value should be returned for the length indicating the needed bytes. The disass_cb is then called again with more bytes. If the instruction is rejected a length of 0 should be returned and the string should be set to NULL.


Exception Related Methods

register_exception_before_cb is used to register a callback that will be called before an exception or interrupt is taken. The exception_number can be used to select a callback on a specific exception, with the same number as used in the exception interface. If all exceptions should be subscribed to, the CPU_Exception_All constant can be used. The callback signature looks like this:

typedef void (*cpu_exception_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        exception_handle_t *exception_handle,
        lang_void *user_data);

The obj is the object registering the callback and cpu is the processor that takes the exception or receives the interrupt. The handle is used to get more architectural information about the exception, for example, see the x86_exception_query for details. The user_data is the callback user data.

No architectural state has been changed in this callback to reflect the exception or interrupt, e.g., the program counter will still be at the faulting instruction. Since an exception can occur while handling an exception it is not always the case that this callback corresponds to the final taken exception. Recursive exception will result in another call to this callback.

register_exception_after_cb is used to register a callback that will be called after an exception was taken. It takes the same arguments as register_exception_before_cb. In this callback the architectural state has been updated, e.g., the program counter points to the first instruction of the exception handler. The callback is of the same type as for the before variant.

register_exception_return_before_cb is used to register a callback that will be called just before an exception handler is done and resumes execution of the normal program code. The callback signature looks like this:

typedef void (*cpu_exception_return_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        exception_return_handle_t *exception_handle,
        lang_void *user_data);

The obj is the object registering the callback and cpu is the processor that takes the exception or receives an interrupt. The handle is used to get more architectural information about the exception. Currently there is no interface available for this.

register_exception_return_after_cb is used to register a callback that will be called after an exception has been executed, e.g., the program counter points to the normal program where execution will continue.


Execution Mode

register_mode_change_cb is used to register a callback that will be called every time the processor changes mode. The supported modes are: user, supervisor, and hypervisor.

The callback signature looks lite this:

typedef void (*cpu_mode_change_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        processor_mode_t old_mode, processor_mode_t new_mode,
        lang_void *user_data);

The obj is the object registering the callback and cpu is the processor that changes mode. The old_mode is the mode before the change and new_mode is the mode after the change.


Control Registers

register_control_register_read_before_cb is used to register a callback that will be called before every control register read. The register_number is the control register number to install the callback on. It is the same number which is used in the int_register interface. The constant CPU_Control_Register_All can be used to subscribe to all control registers.

The callback signature looks like this:

typedef void (*cpu_control_register_read_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        int register_number,
        lang_void *user_data);

The obj is the object registering the callback and cpu is the processor that reads the control register register_number. The user_data is the user data for the callback.

register_control_register_write_before_cb is used to register a callback that will be called before every control register write. The register_number is the control register number to install the callback on. It is the same number which is used in the int_register interface. The constant CPU_Control_Register_All can be used to subscribe to all control registers.

The callback signature looks like this:

typedef void (*cpu_control_register_write_cb_t)(
        conf_object_t *obj, conf_object_t *cpu,
        int register_number,
        uint64 value,
        lang_void *user_data);

The obj is the object registering the callback and cpu is the processor that writes to the control register register_number. The value is the value that will be written. The user_data is the user data for the callback.

Execution Context
Global Context for all methods.

cpu_instruction_query cpu_memory_query