X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fada-tasks.c;h=67aa0c798890aae65c5bbada9635f157ac040681;hb=5f512a7dd0df1205630e9edfaa84f2e9a8fb8771;hp=d0ce5ab1bdf58c5dfd9bb9606bba294f0aa257f0;hpb=e22dccb54c00bd39b48bbcf07cafc13b522ab857;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c index d0ce5ab1bd..67aa0c7988 100644 --- a/gdb/ada-tasks.c +++ b/gdb/ada-tasks.c @@ -1,5 +1,4 @@ -/* Copyright (C) 1992, 1993, 1994, 1997, 1998, 1999, 2000, 2003, 2004, 2005, - 2007, 2008, 2009 Free Software Foundation, Inc. +/* Copyright (C) 1992-2019 Free Software Foundation, Inc. This file is part of GDB. @@ -17,21 +16,31 @@ along with this program. If not, see . */ #include "defs.h" -#include "observer.h" +#include "observable.h" #include "gdbcmd.h" #include "target.h" #include "ada-lang.h" #include "gdbcore.h" #include "inferior.h" #include "gdbthread.h" +#include "progspace.h" +#include "objfiles.h" +#include "cli/cli-style.h" + +static int ada_build_task_list (); /* 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 */ +/* The maximum number of tasks known to the Ada runtime. */ static const int MAX_NUMBER_OF_KNOWN_TASKS = 1000; +/* The name of the variable in the GNAT runtime where the head of a task + chain is saved. This is an alternate mechanism to find the list of known + tasks. */ +#define KNOWN_TASKS_LIST "system__tasking__debug__first_task" + enum task_states { Unactivated, @@ -101,7 +110,7 @@ static const char *long_task_states[] = { /* The index of certain important fields in the Ada Task Control Block record and sub-records. */ -struct tcb_fieldnos +struct atcb_fieldnos { /* Fields in record Ada_Task_Control_Block. */ int common; @@ -114,8 +123,10 @@ struct tcb_fieldnos int priority; int image; int image_len; /* This field may be missing. */ + int activation_link; int call; int ll; + int base_cpu; /* Fields in Task_Primitives.Private_Data. */ int ll_thread; @@ -125,67 +136,194 @@ struct tcb_fieldnos 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). */ +/* This module's per-program-space data. */ -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; +struct ada_tasks_pspace_data +{ + /* Nonzero if the data has been initialized. If set to zero, + it means that the data has either not been initialized, or + has potentially become stale. */ + int initialized_p = 0; -/* 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 ATCB record type. */ + struct type *atcb_type = nullptr; -/* 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 + /* The ATCB "Common" component type. */ + struct type *atcb_common_type = nullptr; + + /* The type of the "ll" field, from the atcb_common_type. */ + struct type *atcb_ll_type = nullptr; + + /* The type of the "call" field, from the atcb_common_type. */ + struct type *atcb_call_type = nullptr; + + /* The index of various fields in the ATCB record and sub-records. */ + struct atcb_fieldnos atcb_fieldno {}; +}; + +/* Key to our per-program-space data. */ +static const struct program_space_key + ada_tasks_pspace_data_handle; + +/* The kind of data structure used by the runtime to store the list + of Ada tasks. */ + +enum ada_known_tasks_kind +{ + /* Use this value when we haven't determined which kind of structure + is being used, or when we need to recompute it. + + We set the value of this enumerate to zero on purpose: This allows + us to use this enumerate in a structure where setting all fields + to zero will result in this kind being set to unknown. */ + ADA_TASKS_UNKNOWN = 0, + + /* This value means that we did not find any task list. Unless + there is a bug somewhere, this means that the inferior does not + use tasking. */ + ADA_TASKS_NOT_FOUND, + + /* This value means that the task list is stored as an array. + This is the usual method, as it causes very little overhead. + But this method is not always used, as it does use a certain + amount of memory, which might be scarse in certain environments. */ + ADA_TASKS_ARRAY, + + /* This value means that the task list is stored as a linked list. + This has more runtime overhead than the array approach, but + also require less memory when the number of tasks is small. */ + ADA_TASKS_LIST, +}; + +/* This module's per-inferior data. */ + +struct ada_tasks_inferior_data +{ + /* The type of data structure used by the runtime to store + the list of Ada tasks. The value of this field influences + the interpretation of the known_tasks_addr field below: + - ADA_TASKS_UNKNOWN: The value of known_tasks_addr hasn't + been determined yet; + - ADA_TASKS_NOT_FOUND: The program probably does not use tasking + and the known_tasks_addr is irrelevant; + - ADA_TASKS_ARRAY: The known_tasks is an array; + - ADA_TASKS_LIST: The known_tasks is a list. */ + enum ada_known_tasks_kind known_tasks_kind = ADA_TASKS_UNKNOWN; + + /* The address of the known_tasks structure. This is where + the runtime stores the information for all Ada tasks. + The interpretation of this field depends on KNOWN_TASKS_KIND + above. */ + CORE_ADDR known_tasks_addr = 0; + + /* Type of elements of the known task. Usually a pointer. */ + struct type *known_tasks_element = nullptr; + + /* Number of elements in the known tasks array. */ + unsigned int known_tasks_length = 0; + + /* When nonzero, this flag indicates that the task_list field + below is up to date. When set to zero, the list has either + not been initialized, or has potentially become stale. */ + bool task_list_valid_p = false; + + /* 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. */ + std::vector task_list; +}; + +/* Key to our per-inferior data. */ +static const struct inferior_key + ada_tasks_inferior_data_handle; + +/* Return a string with TASKNO followed by the task name if TASK_INFO + contains a name. */ + +static std::string +task_to_str (int taskno, const ada_task_info *task_info) +{ + if (task_info->name[0] == '\0') + return string_printf ("%d", taskno); + else + return string_printf ("%d \"%s\"", taskno, task_info->name); +} + +/* Return the ada-tasks module's data for the given program space (PSPACE). + If none is found, add a zero'ed one now. + + This function always returns a valid object. */ + +static struct ada_tasks_pspace_data * +get_ada_tasks_pspace_data (struct program_space *pspace) +{ + struct ada_tasks_pspace_data *data; + + data = ada_tasks_pspace_data_handle.get (pspace); + if (data == NULL) + data = ada_tasks_pspace_data_handle.emplace (pspace); + + return data; +} + +/* Return the ada-tasks module's data for the given inferior (INF). + If none is found, add a zero'ed one now. + + This function always returns a valid object. + + Note that we could use an observer of the inferior-created event + to make sure that the ada-tasks per-inferior data always exists. + But we prefered this approach, as it avoids this entirely as long + as the user does not use any of the tasking features. This is + quite possible, particularly in the case where the inferior does + not use tasking. */ + +static struct ada_tasks_inferior_data * +get_ada_tasks_inferior_data (struct inferior *inf) +{ + struct ada_tasks_inferior_data *data; + + data = ada_tasks_inferior_data_handle.get (inf); + if (data == NULL) + data = ada_tasks_inferior_data_handle.emplace (inf); + + return data; +} + +/* Return the task number of the task whose thread is THREAD, or zero if the task could not be found. */ -static int -ada_get_task_number (ptid_t ptid) +int +ada_get_task_number (thread_info *thread) { - int i; + struct inferior *inf = thread->inf; + struct ada_tasks_inferior_data *data; - 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)) + gdb_assert (inf != NULL); + data = get_ada_tasks_inferior_data (inf); + + for (int i = 0; i < data->task_list.size (); i++) + if (data->task_list[i].ptid == thread->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. */ +/* Return the task number of the task running in inferior INF which + matches TASK_ID , or zero if the task could not be found. */ static int -get_task_number_from_id (CORE_ADDR task_id) +get_task_number_from_id (CORE_ADDR task_id, struct inferior *inf) { - int i; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); - for (i = 0; i < VEC_length (ada_task_info_s, task_list); i++) + for (int i = 0; i < data->task_list.size (); i++) { - struct ada_task_info *task_info = - VEC_index (ada_task_info_s, task_list, i); - - if (task_info->task_id == task_id) + if (data->task_list[i].task_id == task_id) return i + 1; } @@ -198,19 +336,61 @@ get_task_number_from_id (CORE_ADDR task_id) int valid_task_id (int task_num) { - return (task_num > 0 - && task_num <= VEC_length (ada_task_info_s, task_list)); + struct ada_tasks_inferior_data *data; + + ada_build_task_list (); + data = get_ada_tasks_inferior_data (current_inferior ()); + return task_num > 0 && task_num <= data->task_list.size (); } /* Return non-zero iff the task STATE corresponds to a non-terminated task state. */ static int -ada_task_is_alive (struct ada_task_info *task_info) +ada_task_is_alive (const struct ada_task_info *task_info) { return (task_info->state != Terminated); } +/* Search through the list of known tasks for the one whose ptid is + PTID, and return it. Return NULL if the task was not found. */ + +struct ada_task_info * +ada_get_task_info_from_ptid (ptid_t ptid) +{ + struct ada_tasks_inferior_data *data; + + ada_build_task_list (); + data = get_ada_tasks_inferior_data (current_inferior ()); + + for (ada_task_info &task : data->task_list) + { + if (task.ptid == ptid) + return &task; + } + + return NULL; +} + +/* 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) +{ + struct ada_tasks_inferior_data *data; + + ada_build_task_list (); + data = get_ada_tasks_inferior_data (current_inferior ()); + + for (ada_task_info &task : data->task_list) + { + 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. */ @@ -271,87 +451,30 @@ read_fat_string_value (char *dest, struct value *val, int 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); + read_memory (value_address (array_val), (gdb_byte *) 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) +/* Get, from the debugging information, the type description of all types + related to the Ada Task Control Block that are needed in order to + read the list of known tasks in the Ada runtime. If all of the info + needed to do so is found, then save that info in the module's per- + program-space data, and return NULL. Otherwise, if any information + cannot be found, leave the per-program-space data untouched, and + return an error message explaining what was missing (that error + message does NOT need to be deallocated). */ + +const char * +ada_get_tcb_types_info (void) { struct type *type; struct type *common_type; struct type *ll_type; struct type *call_type; - struct tcb_fieldnos fieldnos; + struct atcb_fieldnos fieldnos; + struct ada_tasks_pspace_data *pspace_data; const char *atcb_name = "system__tasking__ada_task_control_block___XVE"; const char *atcb_name_fixed = "system__tasking__ada_task_control_block"; @@ -359,23 +482,33 @@ get_tcb_types_info (struct type **atcb_type, const char *private_data_name = "system__task_primitives__private_data"; const char *entry_call_record_name = "system__tasking__entry_call_record"; + /* ATCB symbols may be found in several compilation units. As we + are only interested in one instance, use standard (literal, + C-like) lookups to get the first match. */ + struct symbol *atcb_sym = - lookup_symbol (atcb_name, NULL, VAR_DOMAIN, NULL); + lookup_symbol_in_language (atcb_name, NULL, STRUCT_DOMAIN, + language_c, NULL).symbol; const struct symbol *common_atcb_sym = - lookup_symbol (common_atcb_name, NULL, VAR_DOMAIN, NULL); + lookup_symbol_in_language (common_atcb_name, NULL, STRUCT_DOMAIN, + language_c, NULL).symbol; const struct symbol *private_data_sym = - lookup_symbol (private_data_name, NULL, VAR_DOMAIN, NULL); + lookup_symbol_in_language (private_data_name, NULL, STRUCT_DOMAIN, + language_c, NULL).symbol; const struct symbol *entry_call_record_sym = - lookup_symbol (entry_call_record_name, NULL, VAR_DOMAIN, NULL); + lookup_symbol_in_language (entry_call_record_name, NULL, STRUCT_DOMAIN, + language_c, NULL).symbol; 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); + atcb_sym = lookup_symbol_in_language (atcb_name_fixed, NULL, + STRUCT_DOMAIN, language_c, + NULL).symbol; if (atcb_sym == NULL || atcb_sym->type == NULL) - error (_("Cannot find Ada_Task_Control_Block type. Aborting")); + return _("Cannot find Ada_Task_Control_Block type"); type = atcb_sym->type; } @@ -388,11 +521,11 @@ get_tcb_types_info (struct type **atcb_type, } if (common_atcb_sym == NULL || common_atcb_sym->type == NULL) - error (_("Cannot find Common_ATCB type. Aborting")); + return _("Cannot find Common_ATCB type"); if (private_data_sym == NULL || private_data_sym->type == NULL) - error (_("Cannot find Private_Data type. Aborting")); + return _("Cannot find Private_Data type"); if (entry_call_record_sym == NULL || entry_call_record_sym->type == NULL) - error (_("Cannot find Entry_Call_Record type. Aborting")); + return _("Cannot find Entry_Call_Record type"); /* Get the type for Ada_Task_Control_Block.Common. */ common_type = common_atcb_sym->type; @@ -413,8 +546,11 @@ get_tcb_types_info (struct type **atcb_type, 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.activation_link = ada_get_field_index (common_type, + "activation_link", 1); fieldnos.call = ada_get_field_index (common_type, "call", 1); fieldnos.ll = ada_get_field_index (common_type, "ll", 0); + fieldnos.base_cpu = ada_get_field_index (common_type, "base_cpu", 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); @@ -430,11 +566,14 @@ get_tcb_types_info (struct type **atcb_type, /* 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; + pspace_data = get_ada_tasks_pspace_data (current_program_space); + pspace_data->initialized_p = 1; + pspace_data->atcb_type = type; + pspace_data->atcb_common_type = common_type; + pspace_data->atcb_ll_type = ll_type; + pspace_data->atcb_call_type = call_type; + pspace_data->atcb_fieldno = fieldnos; + return NULL; } /* Build the PTID of the task from its COMMON_VALUE, which is the "Common" @@ -448,12 +587,16 @@ ptid_from_atcb_common (struct value *common_value) CORE_ADDR lwp = 0; struct value *ll_value; ptid_t ptid; + const struct ada_tasks_pspace_data *pspace_data + = get_ada_tasks_pspace_data (current_program_space); - ll_value = value_field (common_value, fieldno.ll); + ll_value = value_field (common_value, pspace_data->atcb_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)); + if (pspace_data->atcb_fieldno.ll_lwp >= 0) + lwp = value_as_address (value_field (ll_value, + pspace_data->atcb_fieldno.ll_lwp)); + thread = value_as_long (value_field (ll_value, + pspace_data->atcb_fieldno.ll_thread)); ptid = target_get_ada_task_ptid (lwp, thread); @@ -461,7 +604,7 @@ ptid_from_atcb_common (struct value *common_value) } /* 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 + the address of its associated ATCB record), and store the result inside TASK_INFO. */ static void @@ -473,14 +616,25 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) struct value *entry_calls_value; struct value *entry_calls_value_element; int called_task_fieldno = -1; - const char ravenscar_task_name[] = "Ravenscar task"; + static const char ravenscar_task_name[] = "Ravenscar task"; + const struct ada_tasks_pspace_data *pspace_data + = get_ada_tasks_pspace_data (current_program_space); - if (atcb_type == NULL) - get_tcb_types_info (&atcb_type, &atcb_common_type, &atcb_ll_type, - &atcb_call_type, &fieldno); + /* Clear the whole structure to start with, so that everything + is always initialized the same. */ + memset (task_info, 0, sizeof (struct ada_task_info)); - tcb_value = value_from_contents_and_address (atcb_type, NULL, task_id); - common_value = value_field (tcb_value, fieldno.common); + if (!pspace_data->initialized_p) + { + const char *err_msg = ada_get_tcb_types_info (); + + if (err_msg != NULL) + error (_("%s. Aborting"), err_msg); + } + + tcb_value = value_from_contents_and_address (pspace_data->atcb_type, + NULL, task_id); + common_value = value_field (tcb_value, pspace_data->atcb_fieldno.common); /* Fill in the task_id. */ @@ -491,64 +645,97 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) 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 + 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 + 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 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 (pspace_data->atcb_fieldno.image_len == -1) { - if (fieldno.image >= 0) + if (pspace_data->atcb_fieldno.image >= 0) read_fat_string_value (task_info->name, - value_field (common_value, fieldno.image), + value_field (common_value, + pspace_data->atcb_fieldno.image), sizeof (task_info->name) - 1); else - strcpy (task_info->name, ravenscar_task_name); + { + struct bound_minimal_symbol msym; + + msym = lookup_minimal_symbol_by_pc (task_id); + if (msym.minsym) + { + const char *full_name = msym.minsym->linkage_name (); + const char *task_name = full_name; + const char *p; + + /* Strip the prefix. */ + for (p = full_name; *p; p++) + if (p[0] == '_' && p[1] == '_') + task_name = p + 2; + + /* Copy the task name. */ + strncpy (task_info->name, task_name, sizeof (task_info->name)); + task_info->name[sizeof (task_info->name) - 1] = 0; + } + else + { + /* No symbol found. Use a default name. */ + strcpy (task_info->name, ravenscar_task_name); + } + } } else { - int len = value_as_long (value_field (common_value, fieldno.image_len)); + int len = value_as_long + (value_field (common_value, + pspace_data->atcb_fieldno.image_len)); value_as_string (task_info->name, - value_field (common_value, fieldno.image), len); + value_field (common_value, + pspace_data->atcb_fieldno.image), + len); } /* Compute the task state and priority. */ - task_info->state = value_as_long (value_field (common_value, fieldno.state)); + task_info->state = + value_as_long (value_field (common_value, + pspace_data->atcb_fieldno.state)); task_info->priority = - value_as_long (value_field (common_value, fieldno.priority)); + value_as_long (value_field (common_value, + pspace_data->atcb_fieldno.priority)); /* If the ATCB contains some information about the parent task, then compute it as well. Otherwise, zero. */ - if (fieldno.parent >= 0) + if (pspace_data->atcb_fieldno.parent >= 0) task_info->parent = - value_as_address (value_field (common_value, fieldno.parent)); - else - task_info->parent = 0; - + value_as_address (value_field (common_value, + pspace_data->atcb_fieldno.parent)); - /* If the ATCB contains some information about entry calls, then - compute the "called_task" as well. Otherwise, zero. */ + /* If the task is in an entry call waiting for another task, + then determine which task it is. */ - if (fieldno.atc_nesting_level > 0 && fieldno.entry_calls > 0) + if (task_info->state == Entry_Caller_Sleep + && pspace_data->atcb_fieldno.atc_nesting_level > 0 + && pspace_data->atcb_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); + atc_nesting_level_value = + value_field (tcb_value, pspace_data->atcb_fieldno.atc_nesting_level); entry_calls_value = - ada_coerce_to_simple_array_ptr (value_field (tcb_value, - fieldno.entry_calls)); + ada_coerce_to_simple_array_ptr + (value_field (tcb_value, pspace_data->atcb_fieldno.entry_calls)); entry_calls_value_element = - value_subscript (entry_calls_value, atc_nesting_level_value); + value_subscript (entry_calls_value, + value_as_long (atc_nesting_level_value)); called_task_fieldno = ada_get_field_index (value_type (entry_calls_value_element), "called_task", 0); @@ -556,34 +743,37 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) 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. */ + /* If the ATCB contains some information about RV callers, then + compute the "caller_task". Otherwise, leave it as zero. */ - task_info->caller_task = 0; - if (fieldno.call >= 0) + if (pspace_data->atcb_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)); + value_as_address (value_field (common_value, + pspace_data->atcb_fieldno.call)); struct value *call_val; if (call != 0) { call_val = - value_from_contents_and_address (atcb_call_type, NULL, call); + value_from_contents_and_address (pspace_data->atcb_call_type, + NULL, call); task_info->caller_task = - value_as_address (value_field (call_val, fieldno.call_self)); + value_as_address + (value_field (call_val, pspace_data->atcb_fieldno.call_self)); } } - /* And finally, compute the task ptid. */ + task_info->base_cpu + = value_as_long (value_field (common_value, + pspace_data->atcb_fieldno.base_cpu)); + /* And finally, compute the task ptid. Note that there is not point + in computing it if the task is no longer alive, in which case + it is good enough to set its ptid to the null_ptid. */ if (ada_task_is_alive (task_info)) task_info->ptid = ptid_from_atcb_common (common_value); else @@ -591,188 +781,451 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) } /* Read the ATCB info of the given task (identified by TASK_ID), and - add the result to the TASK_LIST. */ + add the result to the given inferior's TASK_LIST. */ static void -add_ada_task (CORE_ADDR task_id) +add_ada_task (CORE_ADDR task_id, struct inferior *inf) { struct ada_task_info task_info; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); read_atcb (task_id, &task_info); - VEC_safe_push (ada_task_info_s, task_list, &task_info); + data->task_list.push_back (task_info); } /* Read the Known_Tasks array from the inferior memory, and store - it in TASK_LIST. Return non-zero upon success. */ + it in the current inferior's TASK_LIST. Return true upon success. */ -static int -read_known_tasks_array (void) +static bool +read_known_tasks_array (struct ada_tasks_inferior_data *data) { - 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); + const int target_ptr_byte = TYPE_LENGTH (data->known_tasks_element); + const int known_tasks_size = target_ptr_byte * data->known_tasks_length; + gdb_byte *known_tasks = (gdb_byte *) 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++) + /* Build a new list by reading the ATCBs from the Known_Tasks array + in the Ada runtime. */ + read_memory (data->known_tasks_addr, known_tasks, known_tasks_size); + for (i = 0; i < data->known_tasks_length; 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); + data->known_tasks_element); if (task_id != 0) - add_ada_task (task_id); + add_ada_task (task_id, current_inferior ()); } - /* 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 true; +} + +/* Read the known tasks from the inferior memory, and store it in + the current inferior's TASK_LIST. Return true upon success. */ - return 1; +static bool +read_known_tasks_list (struct ada_tasks_inferior_data *data) +{ + const int target_ptr_byte = TYPE_LENGTH (data->known_tasks_element); + gdb_byte *known_tasks = (gdb_byte *) alloca (target_ptr_byte); + CORE_ADDR task_id; + const struct ada_tasks_pspace_data *pspace_data + = get_ada_tasks_pspace_data (current_program_space); + + /* Sanity check. */ + if (pspace_data->atcb_fieldno.activation_link < 0) + return false; + + /* Build a new list by reading the ATCBs. Read head of the list. */ + read_memory (data->known_tasks_addr, known_tasks, target_ptr_byte); + task_id = extract_typed_address (known_tasks, data->known_tasks_element); + while (task_id != 0) + { + struct value *tcb_value; + struct value *common_value; + + add_ada_task (task_id, current_inferior ()); + + /* Read the chain. */ + tcb_value = value_from_contents_and_address (pspace_data->atcb_type, + NULL, task_id); + common_value = value_field (tcb_value, pspace_data->atcb_fieldno.common); + task_id = value_as_address + (value_field (common_value, + pspace_data->atcb_fieldno.activation_link)); + } + + return true; } -/* 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. */ +/* Set all fields of the current inferior ada-tasks data pointed by DATA. + Do nothing if those fields are already set and still up to date. */ -int -ada_build_task_list (int warn_if_null) +static void +ada_tasks_inferior_data_sniffer (struct ada_tasks_inferior_data *data) { - if (!target_has_stack) - error (_("Cannot inspect Ada tasks when program is not running")); + struct bound_minimal_symbol msym; + struct symbol *sym; + + /* Return now if already set. */ + if (data->known_tasks_kind != ADA_TASKS_UNKNOWN) + return; + + /* Try array. */ + + msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL); + if (msym.minsym != NULL) + { + data->known_tasks_kind = ADA_TASKS_ARRAY; + data->known_tasks_addr = BMSYMBOL_VALUE_ADDRESS (msym); + + /* Try to get pointer type and array length from the symtab. */ + sym = lookup_symbol_in_language (KNOWN_TASKS_NAME, NULL, VAR_DOMAIN, + language_c, NULL).symbol; + if (sym != NULL) + { + /* Validate. */ + struct type *type = check_typedef (SYMBOL_TYPE (sym)); + struct type *eltype = NULL; + struct type *idxtype = NULL; + + if (TYPE_CODE (type) == TYPE_CODE_ARRAY) + eltype = check_typedef (TYPE_TARGET_TYPE (type)); + if (eltype != NULL + && TYPE_CODE (eltype) == TYPE_CODE_PTR) + idxtype = check_typedef (TYPE_INDEX_TYPE (type)); + if (idxtype != NULL + && !TYPE_LOW_BOUND_UNDEFINED (idxtype) + && !TYPE_HIGH_BOUND_UNDEFINED (idxtype)) + { + data->known_tasks_element = eltype; + data->known_tasks_length = + TYPE_HIGH_BOUND (idxtype) - TYPE_LOW_BOUND (idxtype) + 1; + return; + } + } + + /* Fallback to default values. The runtime may have been stripped (as + in some distributions), but it is likely that the executable still + contains debug information on the task type (due to implicit with of + Ada.Tasking). */ + data->known_tasks_element = + builtin_type (target_gdbarch ())->builtin_data_ptr; + data->known_tasks_length = MAX_NUMBER_OF_KNOWN_TASKS; + return; + } - if (stale_task_list_p) - read_known_tasks_array (); - if (task_list == NULL) + /* Try list. */ + + msym = lookup_minimal_symbol (KNOWN_TASKS_LIST, NULL, NULL); + if (msym.minsym != NULL) { - if (warn_if_null) - printf_filtered (_("Your application does not use any Ada tasks.\n")); - return 0; + data->known_tasks_kind = ADA_TASKS_LIST; + data->known_tasks_addr = BMSYMBOL_VALUE_ADDRESS (msym); + data->known_tasks_length = 1; + + sym = lookup_symbol_in_language (KNOWN_TASKS_LIST, NULL, VAR_DOMAIN, + language_c, NULL).symbol; + if (sym != NULL && SYMBOL_VALUE_ADDRESS (sym) != 0) + { + /* Validate. */ + struct type *type = check_typedef (SYMBOL_TYPE (sym)); + + if (TYPE_CODE (type) == TYPE_CODE_PTR) + { + data->known_tasks_element = type; + return; + } + } + + /* Fallback to default values. */ + data->known_tasks_element = + builtin_type (target_gdbarch ())->builtin_data_ptr; + data->known_tasks_length = 1; + return; } - return 1; + /* Can't find tasks. */ + + data->known_tasks_kind = ADA_TASKS_NOT_FOUND; + data->known_tasks_addr = 0; } -/* Print a one-line description of the task whose number is TASKNO. - The formatting should fit the "info tasks" array. */ +/* Read the known tasks from the current inferior's memory, and store it + in the current inferior's data TASK_LIST. */ static void -short_task_info (int taskno) +read_known_tasks () { - const struct ada_task_info *const task_info = - VEC_index (ada_task_info_s, task_list, taskno - 1); - int active_task_p; + struct ada_tasks_inferior_data *data = + get_ada_tasks_inferior_data (current_inferior ()); - gdb_assert (task_info != NULL); + /* Step 1: Clear the current list, if necessary. */ + data->task_list.clear (); - /* Print a star if this task is the current task (or the task currently - selected). */ + /* Step 2: do the real work. + 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. */ + ada_tasks_inferior_data_sniffer (data); + gdb_assert (data->known_tasks_kind != ADA_TASKS_UNKNOWN); - 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])); + /* Step 3: Set task_list_valid_p, to avoid re-reading the Known_Tasks + array unless needed. */ + switch (data->known_tasks_kind) + { + case ADA_TASKS_NOT_FOUND: /* Tasking not in use in inferior. */ + break; + case ADA_TASKS_ARRAY: + data->task_list_valid_p = read_known_tasks_array (data); + break; + case ADA_TASKS_LIST: + data->task_list_valid_p = read_known_tasks_list (data); + break; + } +} - /* Finally, print the task name. */ - if (task_info->name[0] != '\0') - printf_filtered (" %s\n", task_info->name); - else - printf_filtered (_(" \n")); +/* Build the task_list by reading the Known_Tasks array from + the inferior, and return the number of tasks in that list + (zero means that the program is not using tasking at all). */ + +static int +ada_build_task_list () +{ + struct ada_tasks_inferior_data *data; + + if (!target_has_stack) + error (_("Cannot inspect Ada tasks when program is not running")); + + data = get_ada_tasks_inferior_data (current_inferior ()); + if (!data->task_list_valid_p) + read_known_tasks (); + + return data->task_list.size (); } -/* Print a list containing a short description of all Ada tasks. */ -/* FIXME: Shouldn't we be using ui_out??? */ +/* Print a table providing a short description of all Ada tasks + running inside inferior INF. If ARG_STR is set, it will be + interpreted as a task number, and the table will be limited to + that task only. */ -static void -info_tasks (int from_tty) +void +print_ada_task_info (struct ui_out *uiout, + const char *arg_str, + struct inferior *inf) { - int taskno; - const int nb_tasks = VEC_length (ada_task_info_s, task_list); + struct ada_tasks_inferior_data *data; + int taskno, nb_tasks; + int taskno_arg = 0; + int nb_columns; - printf_filtered (_(" ID TID P-ID Pri State Name\n")); - - for (taskno = 1; taskno <= nb_tasks; taskno++) - short_task_info (taskno); + if (ada_build_task_list () == 0) + { + uiout->message (_("Your application does not use any Ada tasks.\n")); + return; + } + + if (arg_str != NULL && arg_str[0] != '\0') + taskno_arg = value_as_long (parse_and_eval (arg_str)); + + if (uiout->is_mi_like_p ()) + /* In GDB/MI mode, we want to provide the thread ID corresponding + to each task. This allows clients to quickly find the thread + associated to any task, which is helpful for commands that + take a --thread argument. However, in order to be able to + provide that thread ID, the thread list must be up to date + first. */ + target_update_thread_list (); + + data = get_ada_tasks_inferior_data (inf); + + /* Compute the number of tasks that are going to be displayed + in the output. If an argument was given, there will be + at most 1 entry. Otherwise, there will be as many entries + as we have tasks. */ + if (taskno_arg) + { + if (taskno_arg > 0 && taskno_arg <= data->task_list.size ()) + nb_tasks = 1; + else + nb_tasks = 0; + } + else + nb_tasks = data->task_list.size (); + + nb_columns = uiout->is_mi_like_p () ? 8 : 7; + ui_out_emit_table table_emitter (uiout, nb_columns, nb_tasks, "tasks"); + uiout->table_header (1, ui_left, "current", ""); + uiout->table_header (3, ui_right, "id", "ID"); + { + size_t tid_width = 9; + /* Grown below in case the largest entry is bigger. */ + + if (!uiout->is_mi_like_p ()) + { + for (taskno = 1; taskno <= data->task_list.size (); taskno++) + { + const struct ada_task_info *const task_info + = &data->task_list[taskno - 1]; + + gdb_assert (task_info != NULL); + + tid_width = std::max (tid_width, + 1 + strlen (phex_nz (task_info->task_id, + sizeof (CORE_ADDR)))); + } + } + uiout->table_header (tid_width, ui_right, "task-id", "TID"); + } + /* The following column is provided in GDB/MI mode only because + it is only really useful in that mode, and also because it + allows us to keep the CLI output shorter and more compact. */ + if (uiout->is_mi_like_p ()) + uiout->table_header (4, ui_right, "thread-id", ""); + uiout->table_header (4, ui_right, "parent-id", "P-ID"); + uiout->table_header (3, ui_right, "priority", "Pri"); + uiout->table_header (22, ui_left, "state", "State"); + /* Use ui_noalign for the last column, to prevent the CLI uiout + from printing an extra space at the end of each row. This + is a bit of a hack, but does get the job done. */ + uiout->table_header (1, ui_noalign, "name", "Name"); + uiout->table_body (); + + for (taskno = 1; taskno <= data->task_list.size (); taskno++) + { + const struct ada_task_info *const task_info = + &data->task_list[taskno - 1]; + int parent_id; + + gdb_assert (task_info != NULL); + + /* If the user asked for the output to be restricted + to one task only, and this is not the task, skip + to the next one. */ + if (taskno_arg && taskno != taskno_arg) + continue; + + ui_out_emit_tuple tuple_emitter (uiout, NULL); + + /* Print a star if this task is the current task (or the task + currently selected). */ + if (task_info->ptid == inferior_ptid) + uiout->field_string ("current", "*"); + else + uiout->field_skip ("current"); + + /* Print the task number. */ + uiout->field_signed ("id", taskno); + + /* Print the Task ID. */ + uiout->field_string ("task-id", phex_nz (task_info->task_id, + sizeof (CORE_ADDR))); + + /* Print the associated Thread ID. */ + if (uiout->is_mi_like_p ()) + { + thread_info *thread = (ada_task_is_alive (task_info) + ? find_thread_ptid (task_info->ptid) + : nullptr); + + if (thread != NULL) + uiout->field_signed ("thread-id", thread->global_num); + else + { + /* This can happen if the thread is no longer alive. */ + uiout->field_skip ("thread-id"); + } + } + + /* Print the ID of the parent task. */ + parent_id = get_task_number_from_id (task_info->parent, inf); + if (parent_id) + uiout->field_signed ("parent-id", parent_id); + else + uiout->field_skip ("parent-id"); + + /* Print the base priority of the task. */ + uiout->field_signed ("priority", task_info->priority); + + /* Print the task current state. */ + if (task_info->caller_task) + uiout->field_fmt ("state", + _("Accepting RV with %-4d"), + get_task_number_from_id (task_info->caller_task, + inf)); + else if (task_info->called_task) + uiout->field_fmt ("state", + _("Waiting on RV with %-3d"), + get_task_number_from_id (task_info->called_task, + inf)); + else + uiout->field_string ("state", task_states[task_info->state]); + + /* Finally, print the task name, without quotes around it, as mi like + is not expecting quotes, and in non mi-like no need for quotes + as there is a specific column for the name. */ + uiout->field_fmt ("name", + (task_info->name[0] != '\0' + ? ui_file_style () + : metadata_style.style ()), + "%s", + (task_info->name[0] != '\0' + ? task_info->name + : _(""))); + + uiout->text ("\n"); + } } -/* Print a detailed description of the Ada task whose ID is TASKNO_STR. */ +/* Print a detailed description of the Ada task whose ID is TASKNO_STR + for the given inferior (INF). */ static void -info_task (char *taskno_str, int from_tty) +info_task (struct ui_out *uiout, const char *taskno_str, struct inferior *inf) { const int taskno = value_as_long (parse_and_eval (taskno_str)); struct ada_task_info *task_info; int parent_taskno = 0; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); + + if (ada_build_task_list () == 0) + { + uiout->message (_("Your application does not use any Ada tasks.\n")); + return; + } - if (taskno <= 0 || taskno > VEC_length (ada_task_info_s, task_list)) + if (taskno <= 0 || taskno > data->task_list.size ()) 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); + task_info = &data->task_list[taskno - 1]; /* Print the Ada task ID. */ - printf_filtered (_("Ada Task: %s\n"), paddr_nz (task_info->task_id)); + printf_filtered (_("Ada Task: %s\n"), + paddress (target_gdbarch (), 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 (_("\n")); + fprintf_styled (gdb_stdout, metadata_style.style (), _("\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)); + printf_filtered (_("Thread: %#lx\n"), task_info->ptid.tid ()); + printf_filtered (_("LWP: %#lx\n"), task_info->ptid.lwp ()); + + /* If set, print the base CPU. */ + if (task_info->base_cpu != 0) + printf_filtered (_("Base CPU: %d\n"), task_info->base_cpu); /* Print who is the parent (if any). */ if (task_info->parent != 0) - parent_taskno = get_task_number_from_id (task_info->parent); + parent_taskno = get_task_number_from_id (task_info->parent, inf); if (parent_taskno) { - struct ada_task_info *parent = - VEC_index (ada_task_info_s, task_list, parent_taskno - 1); + struct ada_task_info *parent = &data->task_list[parent_taskno - 1]; printf_filtered (_("Parent: %d"), parent_taskno); if (parent->name[0] != '\0') @@ -791,13 +1244,13 @@ info_task (char *taskno_str, int from_tty) if (task_info->caller_task) { - target_taskno = get_task_number_from_id (task_info->caller_task); + target_taskno = get_task_number_from_id (task_info->caller_task, inf); printf_filtered (_("State: Accepting rendezvous with %d"), target_taskno); } - else if (task_info->state == Entry_Caller_Sleep && task_info->called_task) + else if (task_info->called_task) { - target_taskno = get_task_number_from_id (task_info->called_task); + target_taskno = get_task_number_from_id (task_info->called_task, inf); printf_filtered (_("State: Waiting on task %d's entry"), target_taskno); } @@ -806,8 +1259,7 @@ info_task (char *taskno_str, int from_tty) if (target_taskno) { - struct ada_task_info *target_task_info = - VEC_index (ada_task_info_s, task_list, target_taskno - 1); + ada_task_info *target_task_info = &data->task_list[target_taskno - 1]; if (target_task_info->name[0] != '\0') printf_filtered (" (%s)", target_task_info->name); @@ -824,17 +1276,14 @@ info_task (char *taskno_str, int from_tty) Does nothing if the program doesn't use Ada tasking. */ static void -info_tasks_command (char *arg, int from_tty) +info_tasks_command (const char *arg, int from_tty) { - const int task_list_built = ada_build_task_list (1); - - if (!task_list_built) - return; + struct ui_out *uiout = current_uiout; if (arg == NULL || *arg == '\0') - info_tasks (from_tty); + print_ada_task_info (uiout, NULL, current_inferior ()); else - info_task (arg, from_tty); + info_task (uiout, arg, current_inferior ()); } /* Print a message telling the user id of the current task. @@ -843,36 +1292,70 @@ info_tasks_command (char *arg, int from_tty) static void display_current_task_id (void) { - const int current_task = ada_get_task_number (inferior_ptid); + const int current_task = ada_get_task_number (inferior_thread ()); if (current_task == 0) printf_filtered (_("[Current task is unknown]\n")); else - printf_filtered (_("[Current task is %d]\n"), current_task); + { + struct ada_tasks_inferior_data *data + = get_ada_tasks_inferior_data (current_inferior ()); + struct ada_task_info *task_info = &data->task_list[current_task - 1]; + + printf_filtered (_("[Current task is %s]\n"), + task_to_str (current_task, task_info).c_str ()); + } } /* 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) +task_command_1 (const char *taskno_str, int from_tty, struct inferior *inf) { const int taskno = value_as_long (parse_and_eval (taskno_str)); struct ada_task_info *task_info; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); - if (taskno <= 0 || taskno > VEC_length (ada_task_info_s, task_list)) + if (taskno <= 0 || taskno > data->task_list.size ()) 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); + task_info = &data->task_list[taskno - 1]; if (!ada_task_is_alive (task_info)) - error (_("Cannot switch to task %d: Task is no longer running"), taskno); + error (_("Cannot switch to task %s: Task is no longer running"), + task_to_str (taskno, task_info).c_str ()); - switch_to_thread (task_info->ptid); + /* On some platforms, the thread list is not updated until the user + performs a thread-related operation (by using the "info threads" + command, for instance). So this thread list may not be up to date + when the user attempts this task switch. Since we cannot switch + to the thread associated to our task if GDB does not know about + that thread, we need to make sure that any new threads gets added + to the thread list. */ + target_update_thread_list (); + + /* Verify that the ptid of the task we want to switch to is valid + (in other words, a ptid that GDB knows about). Otherwise, we will + cause an assertion failure later on, when we try to determine + the ptid associated thread_info data. We should normally never + encounter such an error, but the wrong ptid can actually easily be + computed if target_get_ada_task_ptid has not been implemented for + our target (yet). Rather than cause an assertion error in that case, + it's nicer for the user to just refuse to perform the task switch. */ + thread_info *tp = find_thread_ptid (task_info->ptid); + if (tp == NULL) + error (_("Unable to compute thread ID for task %s.\n" + "Cannot switch to this task."), + task_to_str (taskno, task_info).c_str ()); + + switch_to_thread (tp); ada_find_printable_frame (get_selected_frame (NULL)); - printf_filtered (_("[Switching to task %d]\n"), taskno); + printf_filtered (_("[Switching to task %s]\n"), + task_to_str (taskno, task_info).c_str ()); print_stack_frame (get_selected_frame (NULL), - frame_relative_level (get_selected_frame (NULL)), 1); + frame_relative_level (get_selected_frame (NULL)), + SRC_AND_LOC, 1); } @@ -880,84 +1363,110 @@ task_command_1 (char *taskno_str, int from_tty) Otherwise, switch to the task indicated by TASKNO_STR. */ static void -task_command (char *taskno_str, int from_tty) +task_command (const char *taskno_str, int from_tty) { - const int task_list_built = ada_build_task_list (1); + struct ui_out *uiout = current_uiout; - if (!task_list_built) - return; + if (ada_build_task_list () == 0) + { + uiout->message (_("Your application does not use any Ada tasks.\n")); + 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); - } + task_command_1 (taskno_str, from_tty, current_inferior ()); } -/* Indicate that the task list may have changed, so invalidate the cache. */ +/* Indicate that the given inferior's task list may have changed, + so invalidate the cache. */ static void -ada_task_list_changed (void) +ada_task_list_changed (struct inferior *inf) { - stale_task_list_p = 1; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); + + data->task_list_valid_p = false; +} + +/* Invalidate the per-program-space data. */ + +static void +ada_tasks_invalidate_pspace_data (struct program_space *pspace) +{ + get_ada_tasks_pspace_data (pspace)->initialized_p = 0; +} + +/* Invalidate the per-inferior data. */ + +static void +ada_tasks_invalidate_inferior_data (struct inferior *inf) +{ + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); + + data->known_tasks_kind = ADA_TASKS_UNKNOWN; + data->task_list_valid_p = false; } /* The 'normal_stop' observer notification callback. */ static void -ada_normal_stop_observer (struct bpstats *unused_args, int unused_args2) +ada_tasks_normal_stop_observer (struct bpstats *unused_args, int unused_args2) { /* 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 (); + ada_task_list_changed (current_inferior ()); } /* A routine to be called when the objfiles have changed. */ static void -ada_new_objfile_observer (struct objfile *objfile) +ada_tasks_new_objfile_observer (struct objfile *objfile) { - /* Invalidate all cached data that were extracted from an objfile. */ + struct inferior *inf; - atcb_type = NULL; - atcb_common_type = NULL; - atcb_ll_type = NULL; - atcb_call_type = NULL; + /* Invalidate the relevant data in our program-space data. */ - ada_tasks_check_symbol_table = 1; -} + if (objfile == NULL) + { + /* All objfiles are being cleared, so we should clear all + our caches for all program spaces. */ + struct program_space *pspace; + + for (pspace = program_spaces; pspace != NULL; pspace = pspace->next) + ada_tasks_invalidate_pspace_data (pspace); + } + else + { + /* The associated program-space data might have changed after + this objfile was added. Invalidate all cached data. */ + ada_tasks_invalidate_pspace_data (objfile->pspace); + } -/* Provide a prototype to silence -Wmissing-prototypes. */ -extern initialize_file_ftype _initialize_tasks; + /* Invalidate the per-inferior cache for all inferiors using + this objfile (or, in other words, for all inferiors who have + the same program-space as the objfile's program space). + If all objfiles are being cleared (OBJFILE is NULL), then + clear the caches for all inferiors. */ + + for (inf = inferior_list; inf != NULL; inf = inf->next) + if (objfile == NULL || inf->pspace == objfile->pspace) + ada_tasks_invalidate_inferior_data (inf); +} void _initialize_tasks (void) { /* Attach various observers. */ - observer_attach_normal_stop (ada_normal_stop_observer); - observer_attach_new_objfile (ada_new_objfile_observer); + gdb::observers::normal_stop.attach (ada_tasks_normal_stop_observer); + gdb::observers::new_objfile.attach (ada_tasks_new_objfile_observer); /* Some new commands provided by this module. */ add_info ("tasks", info_tasks_command, - _("Provide information about all known Ada tasks")); + _("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"), +Without argument, this command simply prints the current task ID."), &cmdlist); } -