diff options
Diffstat (limited to 'gdb/ada-tasks.c')
-rw-r--r-- | gdb/ada-tasks.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c new file mode 100644 index 0000000..23dc105 --- /dev/null +++ b/gdb/ada-tasks.c @@ -0,0 +1,806 @@ +/* file ada-tasks.c: Ada tasking control for GDB + Copyright 1997 Free Software Foundation, Inc. + Contributed by Ada Core Technologies, Inc +. + This file is part of GDB. + + [$Id$] + Authors: Roch-Alexandre Nomine Beguin, Arnaud Charlet <charlet@gnat.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + +*/ + +#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 "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" + +/* FIXME: move all this conditional compilation in description + files or in configure.in */ + +#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)) +/* external declarations */ + +extern struct value* find_function_in_inferior (char *); + +/* Global visible variables */ + +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 + +/* The maximum number of tasks known to the Ada runtime */ +const int MAX_NUMBER_OF_KNOWN_TASKS = 1000; + +/* the current task */ +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" +}; + +/* Global internal types */ + +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" +}; + +/* Global internal variables */ + +static int highest_task_num = 0; +int thread_support = 0; /* 1 if the thread library in use is supported */ +static int gdbtk_task_initialization = 0; + +static int add_task_entry (p_task_id, index) + void *p_task_id; + int index; +{ + struct task_entry *new_task_entry = NULL; + struct task_entry *pt; + + highest_task_num++; + new_task_entry = malloc (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 (p_task_id) + 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 (thread) + 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 (p_task_num) + 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 () +{ + struct task_entry *pt, *old_pt; + + pt = task_list; + while (pt) + { + old_pt = pt; + pt = pt->next_task; + free (old_pt); + }; + task_list = NULL; + highest_task_num = 0; +} + +int valid_task_id (task) + int task; +{ + return get_entry_vptr (task) != NULL; +} + +void *get_self_id () +{ + 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; + } + + /* FIXME: calling a function in the inferior with a multithreaded application + is not reliable, so return NULL if there is no safe way to get the current + task */ + return NULL; +} + +int get_current_task () +{ + int result; + + /* FIXME: language_ada should be defined in defs.h */ + /* if (current_language->la_language != language_ada) return -1; */ + + result = get_entry_number (get_self_id ()); + + /* return -1 if not found */ + return result == 0 ? -1 : result; +} + +/* Print detailed information about specified task */ + +static void +info_task (arg, from_tty) + 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; + + /* FIXME: language_ada should be defined in defs.h */ + /* if (current_language->la_language != language_ada) + { + printf_filtered ("The current language does not support tasks.\n"); + return; + } + */ + pt = get_entry_vptr (atoi (arg)); + if (pt == NULL) + { + printf_filtered ("Task %s not found.\n", arg); + return; + } + + temp_task = pt->task_id; + + /* read the atcb in the inferior */ + READ_MEMORY ((CORE_ADDR) temp_task, atcb); + + /* print the Ada task id */ + printf_filtered ("Ada Task: %p\n", temp_task); + + /* print the name of the 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"); + + /* print the thread id */ + + 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); + } + + /* print the parent gdb task id */ + 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); + + /* print the name of the task */ + 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"); + + /* print the base priority of the task */ + printf_filtered ("Base Priority: %d\n", EXTRACT_INT (atcb.priority)); + + /* print the current state of the task */ + + /* check if this task is accepting a rendezvous */ + 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); + + /* print the name of the task */ + 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 + +/* A useful function that shows the alignment of all the fields in the + tasks_fields structure + */ + +print_align () +{ + 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 + +/* Print information about currently known tasks */ + +static void +info_tasks (arg, from_tty) + 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 (); + + /* then we get a list of tasks created */ + + 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); + } + } + + /* Return without printing anything if this function was called in + order to init GDBTK tasking. */ + + if (init_only) return; + + /* print the header */ + +#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 + + /* Now that we have a list of task id's, we can print them */ + pt = task_list; + while (pt) + { + temp_task = pt->task_id; + + /* read the atcb in the inferior */ + READ_MEMORY ((CORE_ADDR) temp_task, atcb); + + /* store the thread id for future use */ + 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 + + /* print a star if this task is the current one */ + if (thread_id) +#if defined (__WIN32__) || defined (SGI) || defined (hpux) + printf_filtered (pt->lwp == thread_id ? "*" : " "); +#else + printf_filtered (pt->thread == thread_id ? "*" : " "); +#endif + + /* print the gdb task id */ + printf_filtered ("%3d", pt->task_num); + + /* print the Ada task id */ +#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 + + /* print the parent gdb task id */ + printf_filtered + (" %4d", get_entry_number (EXTRACT_ADDRESS (atcb.parent))); + + /* print the base priority of the task */ + 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 the thread is terminated but still there, the + stack_base/size values are erroneous. Try to patch it */ + if (pt->stack_per < 0 || pt->stack_per > 100) pt->stack_per = 0; + } + + /* print information about stack space used in the thread */ + 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 /* Who knows... */ + { + size = thr.__stack_size / 1024 / 1024 / 1024; + car = 'G'; + } + printf_filtered (" %4d%c %2d", size, car, pt->stack_per); +next: +#endif + + /* print the current state of the task */ + + /* check if this task is accepting a rendezvous */ + 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 + /* Replace "Runnable" by "Running" if this is the current task */ + printf_filtered (" %-22s", "Running"); + else + printf_filtered (" %-22s", ada_task_states [state]); + } + + /* finally, print the name of the 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 (" %.*s\n", bounds [1], image); + } + else printf_filtered (" <no name>\n"); + + pt = pt->next_task; + } +} + +/* Task list initialization for GDB-Tk. We basically use info_tasks() + to initialize our variables, but abort that function before we + actually print anything. */ + +int +gdbtk_tcl_tasks_initialize () +{ + gdbtk_task_initialization = 1; + info_tasks ("", gdb_stdout); + + return (task_list != NULL); +} + +static void +info_tasks_command (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg == NULL || *arg == '\000') + info_tasks (arg, from_tty); + else + info_task (arg, from_tty); +} + +/* Switch from one thread to another. */ + +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 ()); +} + +/* Switch to a specified task. */ + +static int task_switch (tid, lwpid) + void *tid, *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 (tidstr, from_tty) + 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) + { + /* FIXME: find_printable_frame should be defined in frame.h, and + implemented in ada-lang.c */ + /* find_printable_frame (selected_frame, frame_relative_level (selected_frame));*/ + printf_filtered ("[Switching to task %d]\n", num); + print_stack_frame (selected_frame, frame_relative_level (selected_frame), 1); + } + else + printf_filtered ("Unable to switch to task %d\n", num); +} + +void +_initialize_tasks () +{ + 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); +} |