diff options
author | Joel Brobecker <brobecker@gnat.com> | 2008-10-22 19:45:05 +0000 |
---|---|---|
committer | Joel Brobecker <brobecker@gnat.com> | 2008-10-22 19:45:05 +0000 |
commit | 0ef643c89ca385cfed451d2135cbd4e4d10c46b7 (patch) | |
tree | 1873de06e0e1c39586bcdbe69b7a6e182757bb89 | |
parent | c209f8472e7d7ea6abb109945f2c53a0b9a92d53 (diff) | |
download | gdb-0ef643c89ca385cfed451d2135cbd4e4d10c46b7.zip gdb-0ef643c89ca385cfed451d2135cbd4e4d10c46b7.tar.gz gdb-0ef643c89ca385cfed451d2135cbd4e4d10c46b7.tar.bz2 |
* target.h (struct target_ops): Add new field to_get_ada_task_ptid.
(target_get_ada_task_ptid): New macro.
* target.c (default_get_ada_task_ptid): New function.
(update_current_target): Inherit field default_get_ada_task_ptid.
(update_current_target): Make default_get_ada_task_ptid the default
value for field to_get_ada_task_ptid.
* ada-lang.h (struct task_control_block): Delete. Never used.
(struct task_ptid, task_ptid_t, struct task_entry, task_list):
Likewise.
(struct ada_task_info): New.
(ada_task_is_alive, ada_find_printable_frame)
(ada_task_list_iterator_ftype, iterate_over_live_ada_tasks): Add
declarations.
(ada_build_task_list): Update prototype.
(init_task_list, ada_is_exception_breakpoint): Remove prototypes.
* ada-lang.c (ada_find_printable_frame): Make non-static.
* ada-tasks.c: New file.
* Makefile.in (SFILES): Add ada-tasks.c.
(COMMON_OBS): Add ada-tasks.o.
* linux-thread-db.c (thread_db_find_thread_from_tid)
(thread_db_get_ada_task_ptid): New functions.
(init_thread_db_ops): Set thread_db_ops.to_get_ada_task_ptid.
-rw-r--r-- | gdb/ChangeLog | 25 | ||||
-rw-r--r-- | gdb/Makefile.in | 3 | ||||
-rw-r--r-- | gdb/ada-lang.c | 2 | ||||
-rw-r--r-- | gdb/ada-lang.h | 69 | ||||
-rw-r--r-- | gdb/ada-tasks.c | 991 | ||||
-rw-r--r-- | gdb/linux-thread-db.c | 30 | ||||
-rw-r--r-- | gdb/target.c | 16 | ||||
-rw-r--r-- | gdb/target.h | 9 |
8 files changed, 1105 insertions, 40 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f760632..5892c33 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,28 @@ +2008-10-22 Joel Brobecker <brobecker@adacore.com> + + * target.h (struct target_ops): Add new field to_get_ada_task_ptid. + (target_get_ada_task_ptid): New macro. + * target.c (default_get_ada_task_ptid): New function. + (update_current_target): Inherit field default_get_ada_task_ptid. + (update_current_target): Make default_get_ada_task_ptid the default + value for field to_get_ada_task_ptid. + * ada-lang.h (struct task_control_block): Delete. Never used. + (struct task_ptid, task_ptid_t, struct task_entry, task_list): + Likewise. + (struct ada_task_info): New. + (ada_task_is_alive, ada_find_printable_frame) + (ada_task_list_iterator_ftype, iterate_over_live_ada_tasks): Add + declarations. + (ada_build_task_list): Update prototype. + (init_task_list, ada_is_exception_breakpoint): Remove prototypes. + * ada-lang.c (ada_find_printable_frame): Make non-static. + * ada-tasks.c: New file. + * Makefile.in (SFILES): Add ada-tasks.c. + (COMMON_OBS): Add ada-tasks.o. + * linux-thread-db.c (thread_db_find_thread_from_tid) + (thread_db_get_ada_task_ptid): New functions. + (init_thread_db_ops): Set thread_db_ops.to_get_ada_task_ptid. + 2008-10-22 Tom Tromey <tromey@redhat.com> PR gdb/2506: diff --git a/gdb/Makefile.in b/gdb/Makefile.in index fe569c3..645d901 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -611,7 +611,7 @@ TARGET_FLAGS_TO_PASS = \ # Links made at configuration time should not be specified here, since # SFILES is used in building the distribution archive. -SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ +SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ addrmap.c \ auxv.c ax-general.c ax-gdb.c \ bcache.c \ @@ -786,6 +786,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ dwarf2read.o mipsread.o stabsread.o corefile.o \ dwarf2expr.o dwarf2loc.o dwarf2-frame.o \ ada-lang.o c-lang.o f-lang.o objc-lang.o \ + ada-tasks.o \ ui-out.o cli-out.o \ varobj.o vec.o wrapper.o \ jv-lang.o jv-valprint.o jv-typeprint.o \ diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 2e29770..60ceacc 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -9888,7 +9888,7 @@ is_known_support_routine (struct frame_info *frame) /* Find the first frame that contains debugging information and that is not part of the Ada run-time, starting from FI and moving upward. */ -static void +void ada_find_printable_frame (struct frame_info *fi) { for (; fi != NULL; fi = get_prev_frame (fi)) diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h index 2d13603..ce6be4b 100644 --- a/gdb/ada-lang.h +++ b/gdb/ada-lang.h @@ -196,46 +196,37 @@ enum ada_renaming_category /* Ada task structures. */ -/* Ada task control block, as defined in the GNAT runt-time library. */ - -struct task_control_block +struct ada_task_info { - char state; - CORE_ADDR parent; + /* The PTID of the thread that this task runs on. This ptid is computed + in a target-dependent way from the associated Task Control Block. */ + ptid_t ptid; + + /* The ID of the task. */ + CORE_ADDR task_id; + + /* The name of the task. */ + char name[257]; + + /* The current state of the task. */ + int state; + + /* The priority associated to the task. */ int priority; - char image [32]; - int image_len; /* This field is not always present in the ATCB. */ - CORE_ADDR call; - CORE_ADDR thread; - CORE_ADDR lwp; /* This field is not always present in the ATCB. */ - - /* If the task is waiting on a task entry, this field contains the - task_id of the other task. */ - CORE_ADDR called_task; -}; -struct task_ptid -{ - int pid; /* The Process id */ - long lwp; /* The Light Weight Process id */ - long tid; /* The Thread id */ -}; -typedef struct task_ptid task_ptid_t; + /* If non-zero, the task ID of the parent task. */ + CORE_ADDR parent; -struct task_entry -{ - CORE_ADDR task_id; - struct task_control_block atcb; - int task_num; - int known_tasks_index; - struct task_entry *next_task; - task_ptid_t task_ptid; - int stack_per; -}; + /* If the task is waiting on a task entry, this field contains + the ID of the other task. Zero otherwise. */ + CORE_ADDR called_task; -/* task entry list. */ -extern struct task_entry *task_list; + /* If the task is accepting a rendezvous with another task, this field + contains the ID of the calling task. Zero otherwise. */ + CORE_ADDR caller_task; +}; +int ada_task_is_alive (struct ada_task_info *task); /* Assuming V points to an array of S objects, make sure that it contains at least M objects, updating V and S as necessary. */ @@ -465,6 +456,8 @@ extern enum ada_renaming_category ada_parse_renaming (struct symbol *, const char **, int *, const char **); +extern void ada_find_printable_frame (struct frame_info *fi); + extern char *ada_breakpoint_rewrite (char *, int *); extern char *ada_main_name (void); @@ -473,9 +466,9 @@ extern char *ada_main_name (void); extern int valid_task_id (int); -extern void init_task_list (void); - -extern int ada_is_exception_breakpoint (bpstat bs); +typedef void (ada_task_list_iterator_ftype) (struct ada_task_info *task); +extern void iterate_over_live_ada_tasks + (ada_task_list_iterator_ftype *iterator); extern void ada_adjust_exception_stop (bpstat bs); @@ -491,7 +484,7 @@ extern void ada_print_exception_breakpoint_task (struct breakpoint *); extern void ada_reset_thread_registers (void); -extern int ada_build_task_list (void); +extern int ada_build_task_list (int warn_if_null); extern int ada_exception_catchpoint_p (struct breakpoint *b); diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c new file mode 100644 index 0000000..17671b3 --- /dev/null +++ b/gdb/ada-tasks.c @@ -0,0 +1,991 @@ +/* Copyright (C) 1992, 1993, 1994, 1997, 1998, 1999, 2000, 2003, 2004, + 2005, 2007, 2008 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "observer.h" +#include "gdbcmd.h" +#include "target.h" +#include "ada-lang.h" +#include "gdbcore.h" +#include "inferior.h" +#include "gdbthread.h" + +/* The name of the array in the GNAT runtime where the Ada Task Control + Block of each task is stored. */ +#define KNOWN_TASKS_NAME "system__tasking__debug__known_tasks" + +/* The maximum number of tasks known to the Ada runtime */ +static const int MAX_NUMBER_OF_KNOWN_TASKS = 1000; + +enum task_states +{ + Unactivated, + Runnable, + Terminated, + Activator_Sleep, + Acceptor_Sleep, + Entry_Caller_Sleep, + Async_Select_Sleep, + Delay_Sleep, + Master_Completion_Sleep, + Master_Phase_2_Sleep, + Interrupt_Server_Idle_Sleep, + Interrupt_Server_Blocked_Interrupt_Sleep, + Timer_Server_Sleep, + AST_Server_Sleep, + Asynchronous_Hold, + Interrupt_Server_Blocked_On_Event_Flag +}; + +/* A short description corresponding to each possible task state. */ +static char *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"), + "" +}; + +/* A longer description corresponding to each possible task state. */ +static char *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"), + "" +}; + +/* The index of certain important fields in the Ada Task Control Block + record and sub-records. */ + +struct tcb_fieldnos +{ + /* Fields in record Ada_Task_Control_Block. */ + int common; + int entry_calls; + int atc_nesting_level; + + /* Fields in record Common_ATCB. */ + int state; + int parent; + int priority; + int image; + int image_len; /* This field may be missing. */ + int call; + int ll; + + /* Fields in Task_Primitives.Private_Data. */ + int ll_thread; + int ll_lwp; /* This field may be missing. */ + + /* Fields in Common_ATCB.Call.all. */ + int call_self; +}; + +/* The type description for the ATCB record and subrecords, and + the associated tcb_fieldnos. For efficiency reasons, these are made + static globals so that we can compute them only once the first time + and reuse them later. Set to NULL if the types haven't been computed + yet, or if they may be obsolete (for instance after having loaded + a new binary). */ + +static struct type *atcb_type = NULL; +static struct type *atcb_common_type = NULL; +static struct type *atcb_ll_type = NULL; +static struct type *atcb_call_type = NULL; +static struct tcb_fieldnos fieldno; + +/* Set to 1 when the cached address of System.Tasking.Debug.Known_Tasks + might be stale and so needs to be recomputed. */ +static int ada_tasks_check_symbol_table = 1; + +/* The list of Ada tasks. + + Note: To each task we associate a number that the user can use to + reference it - this number is printed beside each task in the tasks + info listing displayed by "info tasks". This number is equal to + its index in the vector + 1. Reciprocally, to compute the index + of a task in the vector, we need to substract 1 from its number. */ +typedef struct ada_task_info ada_task_info_s; +DEF_VEC_O(ada_task_info_s); +static VEC(ada_task_info_s) *task_list = NULL; + +/* When non-zero, this flag indicates that the current task_list + is obsolete, and should be recomputed before it is accessed. */ +static int stale_task_list_p = 1; + +/* Return the task number of the task whose ptid is PTID, or zero + if the task could not be found. */ + +int +ada_get_task_number (ptid_t ptid) +{ + int i; + + for (i=0; i < VEC_length (ada_task_info_s, task_list); i++) + if (ptid_equal (VEC_index (ada_task_info_s, task_list, i)->ptid, ptid)) + return i + 1; + + return 0; /* No matching task found. */ +} + +/* Return the task number of the task that matches TASK_ID, or zero + if the task could not be found. */ + +static int +get_task_number_from_id (CORE_ADDR task_id) +{ + int i; + + for (i = 0; i < VEC_length (ada_task_info_s, task_list); i++) + { + struct ada_task_info *task_info = + VEC_index (ada_task_info_s, task_list, i); + + if (task_info->task_id == task_id) + return i + 1; + } + + /* Task not found. Return 0. */ + return 0; +} + +/* Return non-zero if TASK_NUM is a valid task number. */ + +int +valid_task_id (int task_num) +{ + return (task_num > 0 + && task_num <= VEC_length (ada_task_info_s, task_list)); +} + +/* Return the task info associated to the Environment Task. + This function assumes that the inferior does in fact use tasking. */ + +struct ada_task_info * +ada_get_environment_task (void) +{ + ada_build_task_list (0); + gdb_assert (VEC_length (ada_task_info_s, task_list) > 0); + + /* We use a little bit of insider knowledge to determine which task + is the Environment Task: We know that this task is created first, + and thus should always be task #1, which is at index 0 of the + TASK_LIST. */ + return (VEC_index (ada_task_info_s, task_list, 0)); +} + +/* Call the ITERATOR function once for each Ada task that hasn't been + terminated yet. */ + +void +iterate_over_live_ada_tasks (ada_task_list_iterator_ftype *iterator) +{ + int i, nb_tasks; + struct ada_task_info *task; + + ada_build_task_list (0); + nb_tasks = VEC_length (ada_task_info_s, task_list); + + for (i = 0; i < nb_tasks; i++) + { + task = VEC_index (ada_task_info_s, task_list, i); + if (!ada_task_is_alive (task)) + continue; + iterator (task); + } +} + +/* Extract the contents of the value as a string whose length is LENGTH, + and store the result in DEST. */ + +static void +value_as_string (char *dest, struct value *val, int length) +{ + memcpy (dest, value_contents (val), length); + dest[length] = '\0'; +} + +/* Extract the string image from the fat string corresponding to VAL, + and store it in DEST. If the string length is greater than MAX_LEN, + then truncate the result to the first MAX_LEN characters of the fat + string. */ + +static void +read_fat_string_value (char *dest, struct value *val, int max_len) +{ + struct value *array_val; + struct value *bounds_val; + int len; + + /* The following variables are made static to avoid recomputing them + each time this function is called. */ + static int initialize_fieldnos = 1; + static int array_fieldno; + static int bounds_fieldno; + static int upper_bound_fieldno; + + /* Get the index of the fields that we will need to read in order + to extract the string from the fat string. */ + if (initialize_fieldnos) + { + struct type *type = value_type (val); + struct type *bounds_type; + + array_fieldno = ada_get_field_index (type, "P_ARRAY", 0); + bounds_fieldno = ada_get_field_index (type, "P_BOUNDS", 0); + + bounds_type = TYPE_FIELD_TYPE (type, bounds_fieldno); + if (TYPE_CODE (bounds_type) == TYPE_CODE_PTR) + bounds_type = TYPE_TARGET_TYPE (bounds_type); + if (TYPE_CODE (bounds_type) != TYPE_CODE_STRUCT) + error (_("Unknown task name format. Aborting")); + upper_bound_fieldno = ada_get_field_index (bounds_type, "UB0", 0); + + initialize_fieldnos = 0; + } + + /* Get the size of the task image by checking the value of the bounds. + The lower bound is always 1, so we only need to read the upper bound. */ + bounds_val = value_ind (value_field (val, bounds_fieldno)); + len = value_as_long (value_field (bounds_val, upper_bound_fieldno)); + + /* Make sure that we do not read more than max_len characters... */ + if (len > max_len) + len = max_len; + + /* Extract LEN characters from the fat string. */ + array_val = value_ind (value_field (val, array_fieldno)); + read_memory (VALUE_ADDRESS (array_val), dest, len); + + /* Add the NUL character to close the string. */ + dest[len] = '\0'; +} + +/* Return the address of the Known_Tasks array maintained in + the Ada Runtime. Return NULL if the array could not be found, + meaning that the inferior program probably does not use tasking. + + In order to provide a fast response time, this function caches + the Known_Tasks array address after the lookup during the first + call. Subsequent calls will simply return this cached address. */ + +static CORE_ADDR +get_known_tasks_addr (void) +{ + static CORE_ADDR known_tasks_addr = 0; + + if (ada_tasks_check_symbol_table) + { + struct symbol *sym; + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL); + if (msym != NULL) + known_tasks_addr = SYMBOL_VALUE_ADDRESS (msym); + else + { + if (target_lookup_symbol (KNOWN_TASKS_NAME, &known_tasks_addr) != 0) + return 0; + } + + /* FIXME: brobecker 2003-03-05: Here would be a much better place + to attach the ada-tasks observers, instead of doing this + unconditionaly in _initialize_tasks. This would avoid an + unecessary notification when the inferior does not use tasking + or as long as the user does not use the ada-tasks commands. + Unfortunately, this is not possible for the moment: the current + code resets ada__tasks_check_symbol_table back to 1 whenever + symbols for a new program are being loaded. If we place the + observers intialization here, we will end up adding new observers + everytime we do the check for Ada tasking-related symbols + above. This would currently have benign effects, but is still + undesirable. The cleanest approach is probably to create a new + observer to notify us when the user is debugging a new program. + We would then reset ada__tasks_check_symbol_table back to 1 + during the notification, but also detach all observers. + BTW: observers are probably not reentrant, so detaching during + a notification may not be the safest thing to do... Sigh... + But creating the new observer would be a good idea in any case, + since this allow us to make ada__tasks_check_symbol_table + static, which is a good bonus. */ + ada_tasks_check_symbol_table = 0; + } + + return known_tasks_addr; +} + +/* Get from the debugging information the type description of all types + related to the Ada Task Control Block that will be needed in order to + read the list of known tasks in the Ada runtime. Also return the + associated ATCB_FIELDNOS. + + Error handling: Any data missing from the debugging info will cause + an error to be raised, and none of the return values to be set. + Users of this function can depend on the fact that all or none of the + return values will be set. */ + +static void +get_tcb_types_info (struct type **atcb_type, + struct type **atcb_common_type, + struct type **atcb_ll_type, + struct type **atcb_call_type, + struct tcb_fieldnos *atcb_fieldnos) +{ + struct type *type; + struct type *common_type; + struct type *ll_type; + struct type *call_type; + struct tcb_fieldnos fieldnos; + + const char *atcb_name = "system__tasking__ada_task_control_block___XVE"; + const char *atcb_name_fixed = "system__tasking__ada_task_control_block"; + const char *common_atcb_name = "system__tasking__common_atcb"; + const char *private_data_name = "system__task_primitives__private_data"; + const char *entry_call_record_name = "system__tasking__entry_call_record"; + + struct symbol *atcb_sym = + lookup_symbol (atcb_name, NULL, VAR_DOMAIN, NULL); + const struct symbol *common_atcb_sym = + lookup_symbol (common_atcb_name, NULL, VAR_DOMAIN, NULL); + const struct symbol *private_data_sym = + lookup_symbol (private_data_name, NULL, VAR_DOMAIN, NULL); + const struct symbol *entry_call_record_sym = + lookup_symbol (entry_call_record_name, NULL, VAR_DOMAIN, NULL); + + if (atcb_sym == NULL || atcb_sym->type == NULL) + { + /* In Ravenscar run-time libs, the ATCB does not have a dynamic + size, so the symbol name differs. */ + atcb_sym = lookup_symbol (atcb_name_fixed, NULL, VAR_DOMAIN, NULL); + + if (atcb_sym == NULL || atcb_sym->type == NULL) + error (_("Cannot find Ada_Task_Control_Block type. Aborting")); + + type = atcb_sym->type; + } + else + { + /* Get a static representation of the type record + Ada_Task_Control_Block. */ + type = atcb_sym->type; + type = ada_template_to_fixed_record_type_1 (type, NULL, 0, NULL, 0); + } + + if (common_atcb_sym == NULL || common_atcb_sym->type == NULL) + error (_("Cannot find Common_ATCB type. Aborting")); + if (private_data_sym == NULL || private_data_sym->type == NULL) + error (_("Cannot find Private_Data type. Aborting")); + if (entry_call_record_sym == NULL || entry_call_record_sym->type == NULL) + error (_("Cannot find Entry_Call_Record type. Aborting")); + + /* Get the type for Ada_Task_Control_Block.Common. */ + common_type = common_atcb_sym->type; + + /* Get the type for Ada_Task_Control_Bloc.Common.Call.LL. */ + ll_type = private_data_sym->type; + + /* Get the type for Common_ATCB.Call.all. */ + call_type = entry_call_record_sym->type; + + /* Get the field indices. */ + fieldnos.common = ada_get_field_index (type, "common", 0); + fieldnos.entry_calls = ada_get_field_index (type, "entry_calls", 1); + fieldnos.atc_nesting_level = + ada_get_field_index (type, "atc_nesting_level", 1); + fieldnos.state = ada_get_field_index (common_type, "state", 0); + fieldnos.parent = ada_get_field_index (common_type, "parent", 1); + fieldnos.priority = ada_get_field_index (common_type, "base_priority", 0); + fieldnos.image = ada_get_field_index (common_type, "task_image", 1); + fieldnos.image_len = ada_get_field_index (common_type, "task_image_len", 1); + fieldnos.call = ada_get_field_index (common_type, "call", 1); + fieldnos.ll = ada_get_field_index (common_type, "ll", 0); + fieldnos.ll_thread = ada_get_field_index (ll_type, "thread", 0); + fieldnos.ll_lwp = ada_get_field_index (ll_type, "lwp", 1); + fieldnos.call_self = ada_get_field_index (call_type, "self", 0); + + /* On certain platforms such as x86-windows, the "lwp" field has been + named "thread_id". This field will likely be renamed in the future, + but we need to support both possibilities to avoid an unnecessary + dependency on a recent compiler. We therefore try locating the + "thread_id" field in place of the "lwp" field if we did not find + the latter. */ + if (fieldnos.ll_lwp < 0) + fieldnos.ll_lwp = ada_get_field_index (ll_type, "thread_id", 1); + + /* Set all the out parameters all at once, now that we are certain + that there are no potential error() anymore. */ + *atcb_type = type; + *atcb_common_type = common_type; + *atcb_ll_type = ll_type; + *atcb_call_type = call_type; + *atcb_fieldnos = fieldnos; +} + +/* Build the PTID of the task from its COMMON_VALUE, which is the "Common" + component of its ATCB record. This PTID needs to match the PTID used + by the thread layer. */ + +static ptid_t +ptid_from_atcb_common (struct value *common_value) +{ + long thread = 0; + CORE_ADDR lwp = 0; + struct value *ll_value; + ptid_t ptid; + + ll_value = value_field (common_value, fieldno.ll); + + if (fieldno.ll_lwp >= 0) + lwp = value_as_address (value_field (ll_value, fieldno.ll_lwp)); + thread = value_as_long (value_field (ll_value, fieldno.ll_thread)); + + ptid = target_get_ada_task_ptid (lwp, thread); + + return ptid; +} + +/* Read the ATCB data of a given task given its TASK_ID (which is in practice + the address of its assocated ATCB record), and store the result inside + TASK_INFO. */ + +static void +read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) +{ + struct value *tcb_value; + struct value *common_value; + struct value *atc_nesting_level_value; + struct value *entry_calls_value; + struct value *entry_calls_value_element; + int called_task_fieldno = -1; + const char ravenscar_task_name[] = "Ravenscar task"; + + if (atcb_type == NULL) + get_tcb_types_info (&atcb_type, &atcb_common_type, &atcb_ll_type, + &atcb_call_type, &fieldno); + + tcb_value = value_from_contents_and_address (atcb_type, NULL, task_id); + common_value = value_field (tcb_value, fieldno.common); + + /* Fill in the task_id. */ + + task_info->task_id = task_id; + + /* Compute the name of the task. + + Depending on the GNAT version used, the task image is either a fat + string, or a thin array of characters. Older versions of GNAT used + to use fat strings, and therefore did not need an extra field in + the ATCB to store the string length. For efficiency reasons, newer + versions of GNAT replaced the fat string by a static buffer, but this + also required the addition of a new field named "Image_Len" containing + the length of the task name. The method used to extract the task name + is selected depending on the existence of this field. + + In some run-time libs (e.g. Ravenscar), the name is not in the ATCB; + we may want to get it from the first user frame of the stack. For now, + we just give a dummy name. */ + + if (fieldno.image_len == -1) + { + if (fieldno.image >= 0) + read_fat_string_value (task_info->name, + value_field (common_value, fieldno.image), + sizeof (task_info->name) - 1); + else + strcpy (task_info->name, ravenscar_task_name); + } + else + { + int len = value_as_long (value_field (common_value, fieldno.image_len)); + + value_as_string (task_info->name, + value_field (common_value, fieldno.image), len); + } + + /* Compute the task state and priority. */ + + task_info->state = value_as_long (value_field (common_value, fieldno.state)); + task_info->priority = + value_as_long (value_field (common_value, fieldno.priority)); + + /* If the ATCB contains some information about the parent task, + then compute it as well. Otherwise, zero. */ + + if (fieldno.parent >= 0) + task_info->parent = + value_as_address (value_field (common_value, fieldno.parent)); + else + task_info->parent = 0; + + + /* If the ATCB contains some information about entry calls, then + compute the "called_task" as well. Otherwise, zero. */ + + if (fieldno.atc_nesting_level > 0 && fieldno.entry_calls > 0) + { + /* Let My_ATCB be the Ada task control block of a task calling the + entry of another task; then the Task_Id of the called task is + in My_ATCB.Entry_Calls (My_ATCB.ATC_Nesting_Level).Called_Task. */ + atc_nesting_level_value = value_field (tcb_value, + fieldno.atc_nesting_level); + entry_calls_value = + ada_coerce_to_simple_array_ptr (value_field (tcb_value, + fieldno.entry_calls)); + entry_calls_value_element = + value_subscript (entry_calls_value, atc_nesting_level_value); + called_task_fieldno = + ada_get_field_index (value_type (entry_calls_value_element), + "called_task", 0); + task_info->called_task = + value_as_address (value_field (entry_calls_value_element, + called_task_fieldno)); + } + else + { + task_info->called_task = 0; + } + + /* If the ATCB cotnains some information about RV callers, + then compute the "caller_task". Otherwise, zero. */ + + task_info->caller_task = 0; + if (fieldno.call >= 0) + { + /* Get the ID of the caller task from Common_ATCB.Call.all.Self. + If Common_ATCB.Call is null, then there is no caller. */ + const CORE_ADDR call = + value_as_address (value_field (common_value, fieldno.call)); + struct value *call_val; + + if (call != 0) + { + call_val = + value_from_contents_and_address (atcb_call_type, NULL, call); + task_info->caller_task = + value_as_address (value_field (call_val, fieldno.call_self)); + } + } + + /* And finally, compute the task ptid. */ + + if (ada_task_is_alive (task_info)) + task_info->ptid = ptid_from_atcb_common (common_value); + else + task_info->ptid = null_ptid; +} + +/* Read the ATCB info of the given task (identified by TASK_ID), and + add the result to the TASK_LIST. */ + +static void +add_ada_task (CORE_ADDR task_id) +{ + struct ada_task_info task_info; + + read_atcb (task_id, &task_info); + VEC_safe_push (ada_task_info_s, task_list, &task_info); +} + +/* Read the Known_Tasks array from the inferior memory, and store + it in TASK_LIST. Return non-zero upon success. */ + +static int +read_known_tasks_array (void) +{ + const int target_ptr_byte = + gdbarch_ptr_bit (current_gdbarch) / TARGET_CHAR_BIT; + const CORE_ADDR known_tasks_addr = get_known_tasks_addr (); + const int known_tasks_size = target_ptr_byte * MAX_NUMBER_OF_KNOWN_TASKS; + gdb_byte *known_tasks = alloca (known_tasks_size); + int i; + + /* Step 1: Clear the current list, if necessary. */ + VEC_truncate (ada_task_info_s, task_list, 0); + + /* If the application does not use task, then no more needs to be done. + It is important to have the task list cleared (see above) before we + return, as we don't want a stale task list to be used... This can + happen for instance when debugging a non-multitasking program after + having debugged a multitasking one. */ + if (known_tasks_addr == 0) + return 0; + + /* Step 2: Build a new list by reading the ATCBs from the Known_Tasks + array in the Ada runtime. */ + read_memory (known_tasks_addr, known_tasks, known_tasks_size); + for (i = 0; i < MAX_NUMBER_OF_KNOWN_TASKS; i++) + { + struct type *data_ptr_type = + builtin_type (current_gdbarch)->builtin_data_ptr; + CORE_ADDR task_id = + extract_typed_address (known_tasks + i * target_ptr_byte, + data_ptr_type); + + if (task_id != 0) + add_ada_task (task_id); + } + + /* Step 3: Unset stale_task_list_p, to avoid re-reading the Known_Tasks + array unless needed. Then report a success. */ + stale_task_list_p = 0; + + return 1; +} + +/* Builds the task_list by reading the Known_Tasks array from + the inferior. Prints an appropriate message and returns non-zero + if it failed to build this list. */ + +int +ada_build_task_list (int warn_if_null) +{ + if (!target_has_stack) + error (_("Cannot inspect Ada tasks when program is not running")); + + if (stale_task_list_p) + read_known_tasks_array (); + + if (task_list == NULL) + { + if (warn_if_null) + printf_filtered (_("Your application does not use any Ada tasks.\n")); + return 0; + } + + return 1; +} + +/* Return non-zero iff the task STATE corresponds to a non-terminated + task state. */ + +int +ada_task_is_alive (struct ada_task_info *task_info) +{ + return (task_info->state != Terminated); +} + +/* Print a one-line description of the task whose number is TASKNO. + The formatting should fit the "info tasks" array. */ + +static void +short_task_info (int taskno) +{ + const struct ada_task_info *const task_info = + VEC_index (ada_task_info_s, task_list, taskno - 1); + int active_task_p; + + gdb_assert (task_info != NULL); + + /* Print a star if this task is the current task (or the task currently + selected). */ + + active_task_p = ptid_equal (task_info->ptid, inferior_ptid); + if (active_task_p) + printf_filtered ("*"); + else + printf_filtered (" "); + + /* Print the task number. */ + printf_filtered ("%3d", taskno); + + /* Print the Task ID. */ + printf_filtered (" %9lx", (long) task_info->task_id); + + /* Print the Task ID of the task parent. */ + printf_filtered (" %4d", get_task_number_from_id (task_info->parent)); + + /* Print the base priority of the task. */ + printf_filtered (" %3d", task_info->priority); + + /* Print the task current state. */ + if (task_info->caller_task) + printf_filtered (_(" Accepting RV with %-4d"), + get_task_number_from_id (task_info->caller_task)); + else if (task_info->state == Entry_Caller_Sleep && task_info->called_task) + printf_filtered (_(" Waiting on RV with %-3d"), + get_task_number_from_id (task_info->called_task)); + else if (task_info->state == Runnable && active_task_p) + /* Replace "Runnable" by "Running" since this is the active task. */ + printf_filtered (" %-22s", "Running"); + else + printf_filtered (" %-22s", task_states[task_info->state]); + + /* Finally, print the task name. */ + if (task_info->name[0] != '\0') + printf_filtered (" %s\n", task_info->name); + else + printf_filtered (_(" <no name>\n")); +} + +/* Print a list containing a short description of all Ada tasks. */ +/* FIXME: Shouldn't we be using ui_out??? */ + +static void +info_tasks (int from_tty) +{ + int taskno; + const int nb_tasks = VEC_length (ada_task_info_s, task_list); + + printf_filtered (_(" ID TID P-ID Pri State Name\n")); + + for (taskno = 1; taskno <= nb_tasks; taskno++) + short_task_info (taskno); +} + +/* Print a detailed description of the Ada task whose ID is TASKNO_STR. */ + +static void +info_task (char *taskno_str, int from_tty) +{ + const int taskno = value_as_long (parse_and_eval (taskno_str)); + struct ada_task_info *task_info; + int parent_taskno = 0; + + if (taskno <= 0 || taskno > VEC_length (ada_task_info_s, task_list)) + error (_("Task ID %d not known. Use the \"info tasks\" command to\n" + "see the IDs of currently known tasks"), taskno); + task_info = VEC_index (ada_task_info_s, task_list, taskno - 1); + + /* Print the Ada task ID. */ + printf_filtered (_("Ada Task: %s\n"), paddr_nz (task_info->task_id)); + + /* Print the name of the task. */ + if (task_info->name[0] != '\0') + printf_filtered (_("Name: %s\n"), task_info->name); + else + printf_filtered (_("<no name>\n")); + + /* Print the TID and LWP. */ + printf_filtered (_("Thread: %#lx\n"), ptid_get_tid (task_info->ptid)); + printf_filtered (_("LWP: %#lx\n"), ptid_get_lwp (task_info->ptid)); + + /* Print who is the parent (if any). */ + if (task_info->parent != 0) + parent_taskno = get_task_number_from_id (task_info->parent); + if (parent_taskno) + { + struct ada_task_info *parent = + VEC_index (ada_task_info_s, task_list, parent_taskno - 1); + + printf_filtered (_("Parent: %d"), parent_taskno); + if (parent->name[0] != '\0') + printf_filtered (" (%s)", parent->name); + printf_filtered ("\n"); + } + else + printf_filtered (_("No parent\n")); + + /* Print the base priority. */ + printf_filtered (_("Base Priority: %d\n"), task_info->priority); + + /* print the task current state. */ + { + int target_taskno = 0; + + if (task_info->caller_task) + { + target_taskno = get_task_number_from_id (task_info->caller_task); + printf_filtered (_("State: Accepting rendezvous with %d"), + target_taskno); + } + else if (task_info->state == Entry_Caller_Sleep && task_info->called_task) + { + target_taskno = get_task_number_from_id (task_info->called_task); + printf_filtered (_("State: Waiting on task %d's entry"), + target_taskno); + } + else + printf_filtered ("State: %s", long_task_states[task_info->state]); + + if (target_taskno) + { + struct ada_task_info *target_task_info = + VEC_index (ada_task_info_s, task_list, target_taskno - 1); + + if (target_task_info->name[0] != '\0') + printf_filtered (" (%s)", target_task_info->name); + } + + printf_filtered ("\n"); + } +} + +/* If ARG is empty or null, then print a list of all Ada tasks. + Otherwise, print detailed information about the task whose ID + is ARG. + + Does nothing if the program doesn't use Ada tasking. */ + +static void +info_tasks_command (char *arg, int from_tty) +{ + const int task_list_built = ada_build_task_list (1); + + if (!task_list_built) + return; + + if (arg == NULL || *arg == '\0') + info_tasks (from_tty); + else + info_task (arg, from_tty); +} + +/* Print a message telling the user id of the current task. + This function assumes that tasking is in use in the inferior. */ + +static void +display_current_task_id (void) +{ + const int current_task = ada_get_task_number (inferior_ptid); + + if (current_task == 0) + printf_filtered (_("[Current task is unknown]\n")); + else + printf_filtered (_("[Current task is %d]\n"), current_task); +} + +/* Parse and evaluate TIDSTR into a task id, and try to switch to + that task. Print an error message if the task switch failed. */ + +static void +task_command_1 (char *taskno_str, int from_tty) +{ + const int taskno = value_as_long (parse_and_eval (taskno_str)); + struct ada_task_info *task_info; + + if (taskno <= 0 || taskno > VEC_length (ada_task_info_s, task_list)) + error (_("Task ID %d not known. Use the \"info tasks\" command to\n" + "see the IDs of currently known tasks"), taskno); + task_info = VEC_index (ada_task_info_s, task_list, taskno - 1); + + if (!ada_task_is_alive (task_info)) + error (_("Cannot switch to task %d: Task is no longer running"), taskno); + + switch_to_thread (task_info->ptid); + ada_find_printable_frame (get_selected_frame (NULL)); + printf_filtered (_("[Switching to task %d]\n"), taskno); + print_stack_frame (get_selected_frame (NULL), + frame_relative_level (get_selected_frame (NULL)), 1); +} + + +/* Print the ID of the current task if TASKNO_STR is empty or NULL. + Otherwise, switch to the task indicated by TASKNO_STR. */ + +static void +task_command (char *taskno_str, int from_tty) +{ + const int task_list_built = ada_build_task_list (1); + + if (!task_list_built) + return; + + if (taskno_str == NULL || taskno_str[0] == '\0') + display_current_task_id (); + else + { + /* Task switching in core files doesn't work, either because: + 1. Thread support is not implemented with core files + 2. Thread support is implemented, but the thread IDs created + after having read the core file are not the same as the ones + that were used during the program life, before the crash. + As a consequence, there is no longer a way for the debugger + to find the associated thead ID of any given Ada task. + So, instead of attempting a task switch without giving the user + any clue as to what might have happened, just error-out with + a message explaining that this feature is not supported. */ + if (!target_has_execution) + error (_("\ +Task switching not supported when debugging from core files\n\ +(use thread support instead)")); + task_command_1 (taskno_str, from_tty); + } +} + +/* Indicate that the task list may have changed, so invalidate the cache. */ + +void +ada_task_list_changed (void) +{ + stale_task_list_p = 1; +} + +/* The 'normal_stop' observer notification callback. */ + +static void +ada_normal_stop_observer (struct bpstats *unused_args) +{ + /* The inferior has been resumed, and just stopped. This means that + our task_list needs to be recomputed before it can be used again. */ + ada_task_list_changed (); +} + +/* A routine to be called when the objfiles have changed. */ + +void +ada_new_objfile_observer (struct objfile *objfile) +{ + /* Invalidate all cached data that were extracted from an objfile. */ + + atcb_type = NULL; + atcb_common_type = NULL; + atcb_ll_type = NULL; + atcb_call_type = NULL; + + ada_tasks_check_symbol_table = 1; +} + +void +_initialize_tasks (void) +{ + /* Attach various observers. */ + observer_attach_normal_stop (ada_normal_stop_observer); + observer_attach_new_objfile (ada_new_objfile_observer); + + /* Some new commands provided by this module. */ + add_info ("tasks", info_tasks_command, + _("Provide information about all known Ada tasks")); + add_cmd ("task", class_run, task_command, + _("Use this command to switch between Ada tasks.\n\ +Without argument, this command simply prints the current task ID"), + &cmdlist); +} + diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 650cbaa..fc85be5 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -1143,6 +1143,35 @@ thread_db_get_thread_local_address (ptid_t ptid, _("TLS not supported on this target")); } +/* Callback routine used to find a thread based on the TID part of + its PTID. */ + +static int +thread_db_find_thread_from_tid (struct thread_info *thread, void *data) +{ + long *tid = (long *) data; + + if (thread->private->tid == *tid) + return 1; + + return 0; +} + +/* Implement the to_get_ada_task_ptid target method for this target. */ + +static ptid_t +thread_db_get_ada_task_ptid (long lwp, long thread) +{ + struct thread_info *thread_info; + + thread_db_find_new_threads (); + thread_info = iterate_over_threads (thread_db_find_thread_from_tid, &thread); + + gdb_assert (thread_info != NULL); + + return (thread_info->ptid); +} + static void init_thread_db_ops (void) { @@ -1163,6 +1192,7 @@ init_thread_db_ops (void) thread_db_ops.to_is_async_p = thread_db_is_async_p; thread_db_ops.to_async = thread_db_async; thread_db_ops.to_async_mask = thread_db_async_mask; + thread_db_ops.to_get_ada_task_ptid = thread_db_get_ada_task_ptid; thread_db_ops.to_magic = OPS_MAGIC; } diff --git a/gdb/target.c b/gdb/target.c index a6bb23e..b5cad35 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -351,6 +351,18 @@ kill_or_be_killed (int from_tty) tcomplain (); } +/* A default implementation for the to_get_ada_task_ptid target method. + + This function builds the PTID by using both LWP and TID as part of + the PTID lwp and tid elements. The pid used is the pid of the + inferior_ptid. */ + +ptid_t +default_get_ada_task_ptid (long lwp, long tid) +{ + return ptid_build (ptid_get_pid (inferior_ptid), lwp, tid); +} + /* Go through the target stack from top to bottom, copying over zero entries in current_target, then filling in still empty entries. In effect, we are doing class inheritance through the pushed target @@ -457,6 +469,7 @@ update_current_target (void) INHERIT (to_get_thread_local_address, t); INHERIT (to_can_execute_reverse, t); /* Do not inherit to_read_description. */ + INHERIT (to_get_ada_task_ptid, t); /* Do not inherit to_search_memory. */ INHERIT (to_magic, t); /* Do not inherit to_memory_map. */ @@ -622,6 +635,9 @@ update_current_target (void) (int (*) (int)) return_one); current_target.to_read_description = NULL; + de_fault (to_get_ada_task_ptid, + (ptid_t (*) (long, long)) + default_get_ada_task_ptid); #undef de_fault /* Finally, position the target-stack beneath the squashed diff --git a/gdb/target.h b/gdb/target.h index cdc898f..9ec8f5d 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -509,6 +509,12 @@ struct target_ops was available. */ const struct target_desc *(*to_read_description) (struct target_ops *ops); + /* Build the PTID of the thread on which a given task is running, + based on LWP and THREAD. These values are extracted from the + task Private_Data section of the Ada Task Control Block, and + their interpretation depends on the target. */ + ptid_t (*to_get_ada_task_ptid) (long lwp, long thread); + /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR. Return 0 if *READPTR is already at the end of the buffer. Return -1 if there is insufficient buffer for a whole entry. @@ -1141,6 +1147,9 @@ extern int target_stopped_data_address_p (struct target_ops *); extern const struct target_desc *target_read_description (struct target_ops *); +#define target_get_ada_task_ptid(lwp, tid) \ + (*current_target.to_get_ada_task_ptid) (lwp,tid) + /* Utility implementation of searching memory. */ extern int simple_search_memory (struct target_ops* ops, CORE_ADDR start_addr, |