The Threads window displays the set of executing contexts on the target processor structured as a set of queues.

To activate the Threads window:

The window is populated using the threads script, which is a JavaScript program store in a file whose file-type property is "Threads Script" (or is called threads.js) and is in the project that is being debugged.

When debugging starts the function init() is called to determine which columns are displayed in the Threads window.

When the application stops on a breakpoint, the function update() is called to create entries in the Threads window corresponding to the columns that have been created together with the saved execution context (register state) of the thread. By double-clicking one of the entries, the debugger displays its saved execution context—to put the debugger back into the default execution context, use Show Next Statement.

Writing the threads script

The threads script controls the Threads window with the Threads object.

The methods Threads.setColumns, Threads.setSortByNumber and Threads.setColor can be called from the function init().

function init()
{
  Threads.setColumns("Name", "Priority", "State", "Time");
  Threads.setSortByNumber("Time");
  Threads.setColor("State", "Ready", "Executing", "Waiting");
}

The above example creates the named columns Name, Priority, State, and Time in the Threads window, with the Time column sorted numerically rather than alphabetically. The states Ready, Executing and Waiting will have yellow, green and red colored pixmaps respectively.

If you don't supply the function init() in the threads script, the Threads window will create the default columns Name, Priority, and State.

The methods Threads.clear(), Threads.newqueue(), and Threads.add() can be called from the function update().

The Threads.clear() method clears the Threads window.

The Threads.newqueue() function takes a string argument and creates a new, top-level entry in the Threads window. Subsequent entries added to this window will go under this entry. If you don't call this, new entries will all be at the top level of the Threads window.

The Threads.add() function takes a variable number of string arguments, which should correspond to the number of columns displayed by the Threads window. The last argument to the Threads.add() function should be an array (possibly empty) containing the registers of the thread or, alternatively, a handle that can be supplied a call to the threads script function getregs(handle), which will return an array when the thread is selected in the Threads window. The array containing the registers should have elements in the same order in which they are displayed in the CPU Registers display—typically this will be in register-number order, e.g., r0, r1, and so on.

function update()
{
  Threads.clear();
  Threads.newqueue("My Tasks");
  Threads.add("Task1", "0", "Executing", "1000", [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
  Threads.add("Task2", "1", "Waiting", "2000", [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
}

The above example will create a fixed output on the Threads window and is here to demonstrate how to call the methods.

To get real thread state, you need to access the debugger from the threads script. To do this, you can use the JavaScript method Debug.evaluate("expression"), which will evaluate the string argument as a debug expression and return the result. The returned result will be an object if you evaluate an expression that denotes a structure or an array. If the expression denotes a structure, each field can be accessed by using its field name.

So, if you have structs in the application as follows…

struct task {
  char *name;
  unsigned char priority;
  char *state;
  unsigned time;
  struct task *next;
  unsigned registers[17];
  unsigned thread_local_storage[4];
};

struct task task2 =
{
  "Task2",
   1,
   "Waiting",
   2000,
   0,
   { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
   { 0,1,2,3 }
};

struct task task1 =
{
  "Task1",
  0,
  "Executing",
  1000,
  &task2,
  { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
  { 0,1,2,3 }
};

…you can update() the Threads window using the following:

task1 = Debug.evaluate("task1");
Threads.add(task1.name, task1.priority, task1.state, task1.time, task1.registers);

You can use pointers and C-style cast to enable linked-list traversal.

var next = Debug.evaluate("&task1");
while (next)
  {
    var xt = Debug.evaluate("*(struct task*)"+next);
    Threads.add(xt.name, xt.priority, xt.state, xt.time, xt.registers);
    next = xt.next;
  }

Note that, if the threads script goes into an endless loop, the debugger—and consequently CrossStudio—will become unresponsive and you will need to kill CrossStudio using a task manager. Therefore, the above loop is better coded as follows:

var next = Debug.evaluate("&task1");
var count = 0;
while (next && count < 10)
  {
    var xt = Debug.evaluate("*(struct task*)"+next);
    Threads.add(xt.name, xt.priority, xt.state, xt.time, xt.registers);
    next = xt.next;
    count++;
  }

You can speed up the Threads window update by not supplying the registers of the thread to the Threads.add() function. To do this, you should supply a handle/pointer to the thread as the last argument to the Threads.add() function. For example:

var next = Debug.evaluate("&task1");
var count = 0;
while (next && count < 10)
  {
    var xt = Debug.evaluate("*(struct task*)"+next);
    Threads.add(xt.name, xt.priority, xt.state, xt.time, next);
    next=xt.next;
    count++;
  }

When the thread is selected, the Threads window will call getregs(x) in the threads script. That function should return the array of registers, for example:

function getregs(x)
{
  return Debug.evaluate("((struct task*)"+x+")->registers");
}

If you use thread local storage, implementing the gettls(x) function enables you to return the base address of the thread local storage, for example:

function gettls(x)
{
  return Debug.evaluate("((struct task*)"+x+")->thread_local_storage");
}

The gettls(x) function can also be called with null as a parameter. In this case you will have to evaluate an expression that returns the current thread local storage, for example:

function gettls(x)
{
  if (x==null)
    x = Debug.evaluate("&currentTask");
  return Debug.evaluate("((struct task*)"+x+")->thread_local_storage");
}

The debugger may require the name of a thread which you can provide by implementing the getname(x) function, for example:

function getname(x)
{
  return Debug.evaluate("((struct task*)"+x+")->name");
}
Adding extra queues to the threads window

You can add extra information to the threads window to display other RTOS queues. In the function init() you can use Threads.setColumns2 to create an additional display in the threads window, for example:

function init()
{
  ...
  Threads.setColumns2("Timers", "Id(Timers)", "Name", "Hook", "Timeout", "Period", "Active");

The first argument is identifier of the queue which is also supplied to Threads.add2 in the function update() as follows

function update()
{
  ...
  Threads.add2("Timers", "0x1FF0A30", "MyTimer", "0x46C8 (Timer50)", "50(550)", "50", "1");

You can avoid updating queues that aren't displayed using the Threads.shown function as follows

function update()
{
  ...
  if (Threads.shown("Timers"))
    Threads.add2("Timers", "0x1FF0A30", "MyTimer", "0x46C8 (Timer50)", "50(550)", "50", "1");