SIMICS OS Awareness Tracker – Intro

In this series of posts, we will explore the various aspects of implementing a OS tracker for Simics OS Awareness (OSA) component. The goal of a tracker is to allow Simics to keep track of the state of an OS. This includes knowing what tasks are present in the system, provide a context to allow source debugging of a task, know which task is executing on which CPU, track the CPU virtual memory to physical memory mapping and more.

Not only is a tracker specific to a given OS, but it must also be aware of the CPU architecture it runs on. This is especially important when the OS makes use of Virtual Memory via MMU. The Page table organization on an ARM processor is very different than an X86. In fact, the X86 could have multiple more (Linear address vs. segmented, etc…). Depending on the OS, the tracker might need to be aware of all those CPU variations. In addition, the tracker might need to keep track of whether the CPU is in Supervisory mode (typical for Kernel operations) or in User Mode (your typical executable program).

With a complex OS like Linux, the tracker might need to keep track of multiple levels of memory management.

From a Simics user perspective, the OS Awareness is represented as a Tree, where each end node is a different thread or program. Intermediate nodes represent different modes / layers of the OS (Kernel vs. User space for example).
Here is an example of a Linux tree:

SIMICS OS Awareness

In this system, there are a number of kernel threads and user space programs running.

In order for the OSA Tracker to represent the state of the OS, it must be able to analyze the OS scheduler’s data structures. By extension, it implies that whoever implements a tracker must understand those data structures and how they are used.
Not every elements of the scheduler needs to be processed by the tracker, only those related with identifying the various contexts and the state of those tasks.

Generally speaking, the tracker cares about:

  • List of threads in the kernel
  • List of Memory Contexts / User Programs
  • Within a User Program, list of threads
  • State of each individual threads/programs
  • For each running thread, which CPU it is executing on

To keep things simple, we will implement a tracker for FreeRTOS, a MIT licensed embedded operating system for small microcontrollers. This will be a single core, MMUless configuration. Thus, we will avoid having to deal with processor specific MMU analysis.

As we described above, before we can start implementing the tracker, we need to understand the data structures we need to track.

In FreeRTOS, the Task Control Block has the following elements the tracker cares about:

typedef struct tskTaskControlBlock
{
…
UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */
…
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created. Facilitates debugging only. */
…
} tskTCB;

For our simple FreeRTOS tracker, we will only consider the list of active tasks for each priority levels and the list of tasks that are delayed.

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */

Here are the relevant definitions for the list structures:

typedef struct xLIST
{
…
volatile UBaseType_t uxNumberOfItems;
ListItem_t * pxIndex; /*< Used to walk through the list. */
MiniListItem_t xListEnd; /*< List item that is always at the end of the list used as a marker. */
…
} List_t;

typedef struct xLIST_ITEM
{
…
struct xLIST_ITEM * pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. */
…
} ListItem_t;

The tracker will need to parse the lists and extract the task names and priorities to discover the tasks present in the system and build the Tree representation.

In addition, the tracker also needs to monitor the list structures to detect if a new task has been created or an existing task has been deleted. This allows the tracker to maintain an accurate Tree even in a dynamic task environment.

In the next installment, we will start the implementation of our tracker.