As mentioned in the previous section, Simics triggers haps for hardware events such as processor privilege level changes, making it easy to change contexts in response to those. It does not trigger haps in response to software events, such as when the currently executing process changes, because it knows nothing about things such as processes. But if you know how the operating system works, you can inspect the information that Simics does provide, and figure out all kinds of stuff.
A process tracker does just that; inspects the simulated operating system, and triggers haps when interesting things occur.
Typically, a new tracker needs to be written for each architecture/OS combination. Simics comes with two trackers:
Their source code can be found in src/extensions. The intention is that this will be helpful if you would like to write a tracker for some other architecture/OS combination.
Note that a tracker is watching over a specific set of processors. This set should typically contain all processors that run an operating system together, and no other processors. That way, processes that migrate between processors will not get lost, and processes running in different operating system instances will not be mixed up. If there is more than one operating system running in the simulation, they will need separate trackers.
A new cpu-mode-tracker can be created like this:
simics> new-cpu-mode-tracker name = tracker0 New cpu mode tracker tracker0 created. simics> tracker0.add-processor cpu0
Creating a linux-process-tracker requires a little more work:
simics> new-linux-process-tracker name = tracker0 New process tracker tracker0 created. simics> tracker0.add-processor cpu0 simics> tracker0.autodetect-parameters
Notice the added command tracker0.autodetect-parameters. This makes the process tracker examine memory to figure out what operating system the target machine is actually running. This means that the target system must be booted before this command is issued. If that is not an option, the OS version can be specified explicitly as an argument to new-linux-process-tracker. The process tracker just created should be suitable to try out the examples below.
If the trackers distributed with Simics do not fit your needs, there are some things to think about when creating a new tracker. (This part is beneficial to read even if you use the trackers shipped with Simics, as it explains the interface to the tracker, with some usage examples.) A tracker must do two things:
(It says "tracker" instead of "process tracker", and "trackee" instead of "process" in the text above because the things being tracked are not necessarily processes. cpu-mode-tracker, for example, tracks processor privilege levels.)
If the tracker tracks processes on a Unix-like operating system, it may additionally do the following:
And if it wants to play nice with automatic configuring of process trackers, there is one more thing to do:
These haps and interfaces are documented in the Reference Manual. The tracker typically implements this functionality by listening to haps such as Core_Mode_Change and Core_Exception, and knowing in which registers and memory locations the operating system stores interesting information.
Clearly, using just the basic interface (the Core_Trackee_Active hap and the tracker interface), it is easy to make a context follow the currently active trackee (this example assumes that there is a tracker called tracker0):
def set_context(user_arg, tracker, tid, cpu, active): if active: cpu.current_context = conf.my_little_context else: cpu.current_context = conf.primary_context current_tid = conf.tracker0.iface.tracker.active_trackee( conf.tracker0, conf.cpu0) SIM_hap_add_callback_obj_index("Core_Trackee_Active", conf.tracker0, 0, set_context, None, current_tid)
Here, current_tid is set to the ID of the trackee that is active when we run this code. SIM_hap_add_callback_obj_index is then used to cause our callback function set_context to be called every time the trackee with this ID becomes active or inactive. For example, if tracker0 is a process tracker, current_tid will be an ID representing the currently executing process (or the operating system—whichever was executing at the time). The callback function then ensures that my_little_context is active whenever that process is active, and that primary_context is active at all other times.
Using the Unix process tracker interface as well (the Core_Trackee_Exec hap and the tracker_unix interface), we can do more complicated things, such as waiting for a specific binary (ifconfig in this example) to be executed, then follow any process that executes it:
def exec_hap(user_arg, tracker, tid, cpu, binary): if binary.endswith("ifconfig"): def active_hap(user_arg, tracker, tid, cpu, active): if active: cpu.current_context = conf.my_little_context else: cpu.current_context = conf.primary_context SIM_hap_add_callback_obj_index("Core_Trackee_Active", tracker, 0, active_hap, None, tid) SIM_hap_add_callback_obj("Core_Trackee_Exec", conf.tracker0, 0, exec_hap, None)Section 21.3 reveals how to do this without having to write scripts.
Switching contexts is not all that can be done with process trackers. Here is another example that prints the number of steps a user process is running continuously in user mode. In this case we are looking at the program "ls".
start_cycle = 0 def exec_hap(user_arg, tracker, tid, cpu, binary): global total_cycles, start_cycle if binary.endswith("ls"): def active_hap(user_arg, tracker, tid, cpu, active): global total_cycles, start_cycle if active: start_cycle = SIM_cycle_count(cpu) else: print "ls ran for", (SIM_cycle_count(cpu) - start_cycle), "cycles" start_cycle = SIM_cycle_count(cpu) SIM_hap_add_callback_obj_index("Core_Trackee_Active", tracker, 0, active_hap, None, tid) SIM_hap_add_callback_obj("Core_Trackee_Exec", conf.tracker0, 0, exec_hap, None)Section 12.3 has an example of how to use process trackers in symbolic debugging.