#include <ctype.h>
#include "defs.h"
#include "command.h"
#include "value.h"
#include "language.h"
#include "inferior.h"
#include "symtab.h"
#include "target.h"
#include "regcache.h"
#include "gdbcore.h"
#if (defined(__alpha__) && defined(__osf__) && !defined(__alpha_vxworks))
#include <sys/procfs.h>
#endif
#if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET))
#include "gregset.h"
#endif
#include "ada-lang.h"
#if defined (VXWORKS_TARGET)
#define THREAD_TO_PID(tid,lwpid) (tid)
#elif defined (linux)
#define THREAD_TO_PID(tid,lwpid) (0)
#elif (defined (sun) && defined (__SVR4))
#define THREAD_TO_PID thread_to_pid
#elif defined (sgi) || defined (__WIN32__) || defined (hpux)
#define THREAD_TO_PID(tid,lwpid) ((int)lwpid)
#else
#define THREAD_TO_PID(tid,lwpid) (0)
#endif
#if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)
#define THREAD_FETCH_REGISTERS dec_thread_fetch_registers
#define GET_CURRENT_THREAD dec_thread_get_current_thread
extern int dec_thread_get_registers (gdb_gregset_t *, gdb_fpregset_t *);
#endif
#if defined (_AIX)
#define THREAD_FETCH_REGISTERS aix_thread_fetch_registers
#define GET_CURRENT_THREAD aix_thread_get_current_thread
#endif
#if defined(VXWORKS_TARGET)
#define GET_CURRENT_THREAD() ((void*)inferior_pid)
#define THREAD_FETCH_REGISTERS() (-1)
#elif defined (sun) && defined (__SVR4)
#define GET_CURRENT_THREAD solaris_thread_get_current_thread
#define THREAD_FETCH_REGISTERS() (-1)
extern void *GET_CURRENT_THREAD ();
#elif defined (_AIX) || (defined(__alpha__) && defined(__osf__))
extern void *GET_CURRENT_THREAD ();
#elif defined (__WIN32__) || defined (hpux)
#define GET_CURRENT_THREAD() (inferior_pid)
#define THREAD_FETCH_REGISTERS() (-1)
#else
#define GET_CURRENT_THREAD() (NULL)
#define THREAD_FETCH_REGISTERS() (-1)
#endif
#define KNOWN_TASKS_NAME "system__tasking__debug__known_tasks"
#define READ_MEMORY(addr, var) read_memory (addr, (char*) &var, sizeof (var))
struct task_entry *task_list = NULL;
int ada__tasks_check_symbol_table = 1;
void *pthread_kern_addr = NULL;
#if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET))
gdb_gregset_t gregset_saved;
gdb_fpregset_t fpregset_saved;
#endif
const int MAX_NUMBER_OF_KNOWN_TASKS = 1000;
int current_task = -1, current_task_id = -1, current_task_index;
void *current_thread, *current_lwp;
char *ada_task_states[] = {
"Unactivated",
"Runnable",
"Terminated",
"Child Activation Wait",
"Accept Statement",
"Waiting on entry call",
"Async Select Wait",
"Delay Sleep",
"Child Termination Wait",
"Wait Child in Term Alt",
"",
"",
"",
"",
"Asynchronous Hold"
};
static char *ada_long_task_states[] = {
"Unactivated",
"Runnable",
"Terminated",
"Waiting for child activation",
"Blocked in accept statement",
"Waiting on entry call",
"Asynchronous Selective Wait",
"Delay Sleep",
"Waiting for children termination",
"Waiting for children in terminate alternative",
"",
"",
"",
"",
"Asynchronous Hold"
};
static int highest_task_num = 0;
int thread_support = 0;
static int gdbtk_task_initialization = 0;
static int
add_task_entry (void *p_task_id, int index)
{
struct task_entry *new_task_entry = NULL;
struct task_entry *pt;
highest_task_num++;
new_task_entry = xmalloc (sizeof (struct task_entry));
new_task_entry->task_num = highest_task_num;
new_task_entry->task_id = p_task_id;
new_task_entry->known_tasks_index = index;
new_task_entry->next_task = NULL;
pt = task_list;
if (pt)
{
while (pt->next_task)
pt = pt->next_task;
pt->next_task = new_task_entry;
pt->stack_per = 0;
}
else
task_list = new_task_entry;
return new_task_entry->task_num;
}
int
get_entry_number (void *p_task_id)
{
struct task_entry *pt;
pt = task_list;
while (pt != NULL)
{
if (pt->task_id == p_task_id)
return pt->task_num;
pt = pt->next_task;
}
return 0;
}
static struct task_entry *
get_thread_entry_vptr (void *thread)
{
struct task_entry *pt;
pt = task_list;
while (pt != NULL)
{
if (pt->thread == thread)
return pt;
pt = pt->next_task;
}
return 0;
}
static struct task_entry *
get_entry_vptr (int p_task_num)
{
struct task_entry *pt;
pt = task_list;
while (pt)
{
if (pt->task_num == p_task_num)
return pt;
pt = pt->next_task;
}
return NULL;
}
void
init_task_list (void)
{
struct task_entry *pt, *old_pt;
pt = task_list;
while (pt)
{
old_pt = pt;
pt = pt->next_task;
xfree (old_pt);
};
task_list = NULL;
highest_task_num = 0;
}
int
valid_task_id (int task)
{
return get_entry_vptr (task) != NULL;
}
void *
get_self_id (void)
{
struct value *val;
void *self_id;
int result;
struct task_entry *ent;
extern int do_not_insert_breakpoints;
#if !((defined(sun) && defined(__SVR4)) || defined(VXWORKS_TARGET) || defined(__WIN32__))
if (thread_support)
#endif
{
ent = get_thread_entry_vptr (GET_CURRENT_THREAD ());
return ent ? ent->task_id : 0;
}
return NULL;
}
int
get_current_task (void)
{
int result;
result = get_entry_number (get_self_id ());
return result == 0 ? -1 : result;
}
static void
info_task (char *arg, int from_tty)
{
void *temp_task;
struct task_entry *pt, *pt2;
void *self_id, *caller;
struct task_fields atcb, atcb2;
struct entry_call call;
int bounds[2];
char image[256];
int num;
pt = get_entry_vptr (atoi (arg));
if (pt == NULL)
{
printf_filtered ("Task %s not found.\n", arg);
return;
}
temp_task = pt->task_id;
READ_MEMORY ((CORE_ADDR) temp_task, atcb);
printf_filtered ("Ada Task: %p\n", temp_task);
if (atcb.image.P_ARRAY != NULL)
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_BOUNDS), bounds);
bounds[1] = EXTRACT_INT (bounds[1]);
read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_ARRAY),
(char *) &image, bounds[1]);
printf_filtered ("Name: %.*s\n", bounds[1], image);
}
else
printf_filtered ("<no name>\n");
if ((long) pt->thread < 65536)
printf_filtered ("Thread: %ld\n", (long int) pt->thread);
else
printf_filtered ("Thread: %p\n", pt->thread);
if ((long) pt->lwp != 0)
{
if ((long) pt->lwp < 65536)
printf_filtered ("LWP: %ld\n", (long int) pt->lwp);
else
printf_filtered ("LWP: %p\n", pt->lwp);
}
num = get_entry_number (EXTRACT_ADDRESS (atcb.parent));
if (num != 0)
{
printf_filtered ("Parent: %d", num);
pt2 = get_entry_vptr (num);
READ_MEMORY ((CORE_ADDR) pt2->task_id, atcb2);
if (atcb2.image.P_ARRAY != NULL)
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_BOUNDS),
bounds);
bounds[1] = EXTRACT_INT (bounds[1]);
read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_ARRAY),
(char *) &image, bounds[1]);
printf_filtered (" (%.*s)\n", bounds[1], image);
}
else
printf_filtered ("\n");
}
else
printf_filtered ("No parent\n");
printf_filtered ("Base Priority: %d\n", EXTRACT_INT (atcb.priority));
if (atcb.call == NULL)
caller = NULL;
else
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.call), call);
caller = EXTRACT_ADDRESS (call.self);
}
if (caller != NULL)
{
num = get_entry_number (caller);
printf_filtered ("Accepting rendezvous with %d", num);
if (num != 0)
{
pt2 = get_entry_vptr (num);
READ_MEMORY ((CORE_ADDR) pt2->task_id, atcb2);
if (atcb2.image.P_ARRAY != NULL)
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_BOUNDS),
bounds);
bounds[1] = EXTRACT_INT (bounds[1]);
read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_ARRAY),
(char *) &image, bounds[1]);
printf_filtered (" (%.*s)\n", bounds[1], image);
}
else
printf_filtered ("\n");
}
else
printf_filtered ("\n");
}
else
printf_filtered ("State: %s\n", ada_long_task_states[atcb.state]);
}
#if 0
print_align (void)
{
struct task_fields tf;
void *tf_base = &(tf);
void *tf_state = &(tf.state);
void *tf_entry_num = &(tf.entry_num);
void *tf_parent = &(tf.parent);
void *tf_priority = &(tf.priority);
void *tf_current_priority = &(tf.current_priority);
void *tf_image = &(tf.image);
void *tf_call = &(tf.call);
void *tf_thread = &(tf.thread);
void *tf_lwp = &(tf.lwp);
printf_filtered ("\n");
printf_filtered ("(tf_base = 0x%x)\n", tf_base);
printf_filtered ("task_fields.entry_num at %3d (0x%x)\n",
tf_entry_num - tf_base, tf_entry_num);
printf_filtered ("task_fields.state at %3d (0x%x)\n",
tf_state - tf_base, tf_state);
printf_filtered ("task_fields.parent at %3d (0x%x)\n",
tf_parent - tf_base, tf_parent);
printf_filtered ("task_fields.priority at %3d (0x%x)\n",
tf_priority - tf_base, tf_priority);
printf_filtered ("task_fields.current_priority at %3d (0x%x)\n",
tf_current_priority - tf_base, tf_current_priority);
printf_filtered ("task_fields.image at %3d (0x%x)\n",
tf_image - tf_base, tf_image);
printf_filtered ("task_fields.call at %3d (0x%x)\n",
tf_call - tf_base, tf_call);
printf_filtered ("task_fields.thread at %3d (0x%x)\n",
tf_thread - tf_base, tf_thread);
printf_filtered ("task_fields.lwp at %3d (0x%x)\n",
tf_lwp - tf_base, tf_lwp);
printf_filtered ("\n");
}
#endif
static void
info_tasks (char *arg, int from_tty)
{
struct value *val;
int i, task_number, state;
void *temp_task, *temp_tasks[MAX_NUMBER_OF_KNOWN_TASKS];
struct task_entry *pt;
void *self_id, *caller, *thread_id = NULL;
struct task_fields atcb;
struct entry_call call;
int bounds[2];
char image[256];
int size;
char car;
#if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)
pthreadTeb_t thr;
gdb_gregset_t regs;
#endif
static struct symbol *sym;
static struct minimal_symbol *msym;
static void *known_tasks_addr = NULL;
int init_only = gdbtk_task_initialization;
gdbtk_task_initialization = 0;
task_number = 0;
if (PIDGET (inferior_ptid) == 0)
{
printf_filtered ("The program is not being run under gdb. ");
printf_filtered ("Use 'run' or 'attach' first.\n");
return;
}
if (ada__tasks_check_symbol_table)
{
thread_support = 0;
#if (defined(__alpha__) && defined(__osf__) & !defined(VXWORKS_TARGET)) || \
defined (_AIX)
thread_support = 1;
#endif
msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL);
if (msym != NULL)
known_tasks_addr = (void *) SYMBOL_VALUE_ADDRESS (msym);
else
#ifndef VXWORKS_TARGET
return;
#else
{
if (target_lookup_symbol (KNOWN_TASKS_NAME, &known_tasks_addr) != 0)
return;
}
#endif
ada__tasks_check_symbol_table = 0;
}
if (known_tasks_addr == NULL)
return;
#if !((defined(sun) && defined(__SVR4)) || defined(VXWORKS_TARGET) || defined(__WIN32__) || defined (hpux))
if (thread_support)
#endif
thread_id = GET_CURRENT_THREAD ();
init_task_list ();
READ_MEMORY ((CORE_ADDR) known_tasks_addr, temp_tasks);
for (i = 0; i < MAX_NUMBER_OF_KNOWN_TASKS; i++)
{
temp_task = EXTRACT_ADDRESS (temp_tasks[i]);
if (temp_task != NULL)
{
task_number = get_entry_number (temp_task);
if (task_number == 0)
task_number = add_task_entry (temp_task, i);
}
}
if (init_only)
return;
#if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)
printf_filtered
(" ID TID P-ID Pri Stack %% State Name\n");
#else
printf_filtered (" ID TID P-ID Pri State Name\n");
#endif
pt = task_list;
while (pt)
{
temp_task = pt->task_id;
READ_MEMORY ((CORE_ADDR) temp_task, atcb);
pt->thread = EXTRACT_ADDRESS (atcb.thread);
#if defined (linux)
pt->lwp = (void *) THREAD_TO_PID (atcb.thread, 0);
#else
pt->lwp = EXTRACT_ADDRESS (atcb.lwp);
#endif
if (thread_id)
#if defined (__WIN32__) || defined (SGI) || defined (hpux)
printf_filtered (pt->lwp == thread_id ? "*" : " ");
#else
printf_filtered (pt->thread == thread_id ? "*" : " ");
#endif
printf_filtered ("%3d", pt->task_num);
#ifndef VXWORKS_TARGET
printf_filtered (" %9lx", (long) temp_task);
#else
#ifdef TARGET_64
printf_filtered (" %#9lx", (unsigned long) pt->thread & 0x3ffffffffff);
#else
printf_filtered (" %#9lx", (long) pt->thread);
#endif
#endif
printf_filtered
(" %4d", get_entry_number (EXTRACT_ADDRESS (atcb.parent)));
printf_filtered (" %3d", EXTRACT_INT (atcb.priority));
#if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)
if (pt->task_num == 1 || atcb.state == Terminated)
{
printf_filtered (" Unknown");
goto next;
}
read_memory ((CORE_ADDR) atcb.thread, &thr, sizeof (thr));
current_thread = atcb.thread;
regs.regs[SP_REGNUM] = 0;
if (dec_thread_get_registers (®s, NULL) == 0)
{
pt->stack_per = (100 * ((long) thr.__stack_base -
regs.regs[SP_REGNUM])) / thr.__stack_size;
if (pt->stack_per < 0 || pt->stack_per > 100)
pt->stack_per = 0;
}
if (thr.__stack_size < 1024 * 1024)
{
size = thr.__stack_size / 1024;
car = 'K';
}
else if (thr.__stack_size < 1024 * 1024 * 1024)
{
size = thr.__stack_size / 1024 / 1024;
car = 'M';
}
else
{
size = thr.__stack_size / 1024 / 1024 / 1024;
car = 'G';
}
printf_filtered (" %4d%c %2d", size, car, pt->stack_per);
next:
#endif
if (atcb.call == NULL)
caller = NULL;
else
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.call), call);
caller = EXTRACT_ADDRESS (call.self);
}
if (caller != NULL)
printf_filtered (" Accepting RV with %-4d",
get_entry_number (caller));
else
{
state = atcb.state;
#if defined (__WIN32__) || defined (SGI) || defined (hpux)
if (state == Runnable && (thread_id && pt->lwp == thread_id))
#else
if (state == Runnable && (thread_id && pt->thread == thread_id))
#endif
printf_filtered (" %-22s", "Running");
else
printf_filtered (" %-22s", ada_task_states[state]);
}
if (atcb.image.P_ARRAY != NULL)
{
READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_BOUNDS),
bounds);
bounds[1] = EXTRACT_INT (bounds[1]);
read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_ARRAY),
(char *) &image, bounds[1]);
printf_filtered (" %.*s\n", bounds[1], image);
}
else
printf_filtered (" <no name>\n");
pt = pt->next_task;
}
}
int
gdbtk_tcl_tasks_initialize (void)
{
gdbtk_task_initialization = 1;
info_tasks ("", gdb_stdout);
return (task_list != NULL);
}
static void
info_tasks_command (char *arg, int from_tty)
{
if (arg == NULL || *arg == '\000')
info_tasks (arg, from_tty);
else
info_task (arg, from_tty);
}
static void
switch_to_thread (ptid_t ptid)
{
if (ptid_equal (ptid, inferior_ptid))
return;
inferior_ptid = ptid;
flush_cached_frames ();
registers_changed ();
stop_pc = read_pc ();
select_frame (get_current_frame ());
}
static int
task_switch (void *tid, void *lwpid)
{
int res = 0, pid;
if (thread_support)
{
flush_cached_frames ();
if (current_task != current_task_id)
{
res = THREAD_FETCH_REGISTERS ();
}
else
{
#if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET))
supply_gregset (&gregset_saved);
supply_fpregset (&fpregset_saved);
#endif
}
if (res == 0)
stop_pc = read_pc ();
select_frame (get_current_frame ());
return res;
}
return -1;
}
static void
task_command (char *tidstr, int from_tty)
{
int num;
struct task_entry *e;
if (!tidstr)
error ("Please specify a task ID. Use the \"info tasks\" command to\n"
"see the IDs of currently known tasks.");
num = atoi (tidstr);
e = get_entry_vptr (num);
if (e == NULL)
error ("Task ID %d not known. Use the \"info tasks\" command to\n"
"see the IDs of currently known tasks.", num);
if (current_task_id == -1)
{
#if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET))
fill_gregset (&gregset_saved, -1);
fill_fpregset (&fpregset_saved, -1);
#endif
current_task_id = get_current_task ();
}
current_task = num;
current_task_index = e->known_tasks_index;
current_thread = e->thread;
current_lwp = e->lwp;
if (task_switch (e->thread, e->lwp) == 0)
{
printf_filtered ("[Switching to task %d]\n", num);
print_stack_frame (deprecated_selected_frame,
frame_relative_level (deprecated_selected_frame), 1);
}
else
printf_filtered ("Unable to switch to task %d\n", num);
}
void
_initialize_tasks (void)
{
static struct cmd_list_element *task_cmd_list = NULL;
extern struct cmd_list_element *cmdlist;
add_info ("tasks", info_tasks_command,
"Without argument: list all known Ada tasks, with status information.\n"
"info tasks n: print detailed information of task n.\n");
add_prefix_cmd ("task", class_run, task_command,
"Use this command to switch between tasks.\n\
The new task ID must be currently known.", &task_cmd_list, "task ", 1, &cmdlist);
}