X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fada-tasks.c;h=dfb6719c09008a6c0c81560a8c038f5d9831326c;hb=b6254dfcc289903d2c453733429931feb0562eb8;hp=71fc25e644c4089e4550c9c80dc4ab78a122d608;hpb=6040a59db5891821c1a73e88cd60adf125879a4f;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c index 71fc25e644..dfb6719c09 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, 2010, 2011 Free Software Foundation, Inc. +/* Copyright (C) 1992-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -24,6 +23,8 @@ #include "gdbcore.h" #include "inferior.h" #include "gdbthread.h" +#include "progspace.h" +#include "objfiles.h" /* The name of the array in the GNAT runtime where the Ada Task Control Block of each task is stored. */ @@ -106,7 +107,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; @@ -131,37 +132,158 @@ 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 atcb_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; -/* 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; + + /* The ATCB "Common" component type. */ + struct type *atcb_common_type; + + /* The type of the "ll" field, from the atcb_common_type. */ + struct type *atcb_ll_type; + + /* The type of the "call" field, from the atcb_common_type. */ + struct type *atcb_call_type; + + /* 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_data *ada_tasks_pspace_data_handle; -/* 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; +/* 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; + + /* 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; + + /* Type of elements of the known task. Usually a pointer. */ + struct type *known_tasks_element; + + /* Number of elements in the known tasks array. */ + unsigned int known_tasks_length; + + /* 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. */ + int task_list_valid_p; + + /* 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. */ + VEC(ada_task_info_s) *task_list; +}; + +/* Key to our per-inferior data. */ +static const struct inferior_data *ada_tasks_inferior_data_handle; + +/* 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 = program_space_data (pspace, ada_tasks_pspace_data_handle); + if (data == NULL) + { + data = XZALLOC (struct ada_tasks_pspace_data); + set_program_space_data (pspace, ada_tasks_pspace_data_handle, data); + } + + 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 = inferior_data (inf, ada_tasks_inferior_data_handle); + if (data == NULL) + { + data = XZALLOC (struct ada_tasks_inferior_data); + set_inferior_data (inf, ada_tasks_inferior_data_handle, data); + } + + return data; +} /* Return the task number of the task whose ptid is PTID, or zero if the task could not be found. */ @@ -170,26 +292,33 @@ int ada_get_task_number (ptid_t ptid) { int i; + struct inferior *inf = find_inferior_pid (ptid_get_pid (ptid)); + 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 (i = 0; i < VEC_length (ada_task_info_s, data->task_list); i++) + if (ptid_equal (VEC_index (ada_task_info_s, data->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. */ +/* 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) { + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); int i; - for (i = 0; i < VEC_length (ada_task_info_s, task_list); i++) + for (i = 0; i < VEC_length (ada_task_info_s, data->task_list); i++) { struct ada_task_info *task_info = - VEC_index (ada_task_info_s, task_list, i); + VEC_index (ada_task_info_s, data->task_list, i); if (task_info->task_id == task_id) return i + 1; @@ -204,9 +333,12 @@ get_task_number_from_id (CORE_ADDR task_id) int valid_task_id (int task_num) { - ada_build_task_list (0); + struct ada_tasks_inferior_data *data; + + ada_build_task_list (); + data = get_ada_tasks_inferior_data (current_inferior ()); return (task_num > 0 - && task_num <= VEC_length (ada_task_info_s, task_list)); + && task_num <= VEC_length (ada_task_info_s, data->task_list)); } /* Return non-zero iff the task STATE corresponds to a non-terminated @@ -226,13 +358,15 @@ iterate_over_live_ada_tasks (ada_task_list_iterator_ftype *iterator) { int i, nb_tasks; struct ada_task_info *task; + struct ada_tasks_inferior_data *data; - ada_build_task_list (0); - nb_tasks = VEC_length (ada_task_info_s, task_list); + ada_build_task_list (); + data = get_ada_tasks_inferior_data (current_inferior ()); + nb_tasks = VEC_length (ada_task_info_s, data->task_list); for (i = 0; i < nb_tasks; i++) { - task = VEC_index (ada_task_info_s, task_list, i); + task = VEC_index (ada_task_info_s, data->task_list, i); if (!ada_task_is_alive (task)) continue; iterator (task); @@ -299,7 +433,7 @@ 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'; @@ -322,7 +456,8 @@ get_tcb_types_info (void) 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"; @@ -412,11 +547,13 @@ get_tcb_types_info (void) /* 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_fieldno = 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; } /* Build the PTID of the task from its COMMON_VALUE, which is the "Common" @@ -430,12 +567,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, atcb_fieldno.ll); + ll_value = value_field (common_value, pspace_data->atcb_fieldno.ll); - if (atcb_fieldno.ll_lwp >= 0) - lwp = value_as_address (value_field (ll_value, atcb_fieldno.ll_lwp)); - thread = value_as_long (value_field (ll_value, atcb_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); @@ -455,13 +596,16 @@ 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) + if (!pspace_data->initialized_p) get_tcb_types_info (); - tcb_value = value_from_contents_and_address (atcb_type, NULL, task_id); - common_value = value_field (tcb_value, atcb_fieldno.common); + 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. */ @@ -482,37 +626,68 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) we may want to get it from the first user frame of the stack. For now, we just give a dummy name. */ - if (atcb_fieldno.image_len == -1) + if (pspace_data->atcb_fieldno.image_len == -1) { - if (atcb_fieldno.image >= 0) + if (pspace_data->atcb_fieldno.image >= 0) read_fat_string_value (task_info->name, - value_field (common_value, atcb_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 = SYMBOL_LINKAGE_NAME (msym.minsym); + 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, - atcb_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, atcb_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, atcb_fieldno.state)); + value_as_long (value_field (common_value, + pspace_data->atcb_fieldno.state)); task_info->priority = - value_as_long (value_field (common_value, atcb_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 (atcb_fieldno.parent >= 0) + if (pspace_data->atcb_fieldno.parent >= 0) task_info->parent = - value_as_address (value_field (common_value, atcb_fieldno.parent)); + value_as_address (value_field (common_value, + pspace_data->atcb_fieldno.parent)); else task_info->parent = 0; @@ -520,16 +695,17 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) /* If the ATCB contains some information about entry calls, then compute the "called_task" as well. Otherwise, zero. */ - if (atcb_fieldno.atc_nesting_level > 0 && atcb_fieldno.entry_calls > 0) + if (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, - atcb_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, - atcb_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, value_as_long (atc_nesting_level_value)); @@ -549,20 +725,23 @@ read_atcb (CORE_ADDR task_id, struct ada_task_info *task_info) then compute the "caller_task". Otherwise, zero. */ task_info->caller_task = 0; - if (atcb_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, atcb_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, atcb_fieldno.call_self)); + value_as_address + (value_field (call_val, pspace_data->atcb_fieldno.call_self)); } } @@ -584,114 +763,189 @@ 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); + VEC_safe_push (ada_task_info_s, data->task_list, &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 non-zero upon success. */ static int -read_known_tasks_array (CORE_ADDR known_tasks_addr) +read_known_tasks_array (struct ada_tasks_inferior_data *data) { - const int target_ptr_byte = - gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT; - const int known_tasks_size = target_ptr_byte * MAX_NUMBER_OF_KNOWN_TASKS; + 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 = alloca (known_tasks_size); int i; /* 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++) + 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 (target_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 ()); } return 1; } /* Read the known tasks from the inferior memory, and store it in - TASK_LIST. Return non-zero upon success. */ + the current inferior's TASK_LIST. Return non-zero upon success. */ static int -read_known_tasks_list (CORE_ADDR known_tasks_addr) +read_known_tasks_list (struct ada_tasks_inferior_data *data) { - const int target_ptr_byte = - gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT; + const int target_ptr_byte = TYPE_LENGTH (data->known_tasks_element); gdb_byte *known_tasks = alloca (target_ptr_byte); - struct type *data_ptr_type = - builtin_type (target_gdbarch)->builtin_data_ptr; CORE_ADDR task_id; + const struct ada_tasks_pspace_data *pspace_data + = get_ada_tasks_pspace_data (current_program_space); /* Sanity check. */ - if (atcb_fieldno.activation_link < 0) + if (pspace_data->atcb_fieldno.activation_link < 0) return 0; /* Build a new list by reading the ATCBs. Read head of the list. */ - read_memory (known_tasks_addr, known_tasks, target_ptr_byte); - task_id = extract_typed_address (known_tasks, data_ptr_type); + 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); + add_ada_task (task_id, current_inferior ()); /* Read the chain. */ - tcb_value = value_from_contents_and_address (atcb_type, NULL, task_id); - common_value = value_field (tcb_value, atcb_fieldno.common); - task_id = value_as_address (value_field (common_value, - atcb_fieldno.activation_link)); + 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 1; } -/* Return the address of the variable NAME that contains all the known - tasks maintained in the Ada Runtime. Return NULL if the variable - could not be found, meaning that the inferior program probably does - not use tasking. */ +/* 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. */ -static CORE_ADDR -get_known_tasks_addr (const char *name) +static void +ada_tasks_inferior_data_sniffer (struct ada_tasks_inferior_data *data) { struct minimal_symbol *msym; + struct symbol *sym; - msym = lookup_minimal_symbol (name, NULL, NULL); - if (msym == NULL) - return 0; + /* Return now if already set. */ + if (data->known_tasks_kind != ADA_TASKS_UNKNOWN) + return; - return SYMBOL_VALUE_ADDRESS (msym); + /* Try array. */ + + msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL); + if (msym != NULL) + { + data->known_tasks_kind = ADA_TASKS_ARRAY; + data->known_tasks_addr = SYMBOL_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); + 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; + } + + + /* Try list. */ + + msym = lookup_minimal_symbol (KNOWN_TASKS_LIST, NULL, NULL); + if (msym != NULL) + { + data->known_tasks_kind = ADA_TASKS_LIST; + data->known_tasks_addr = SYMBOL_VALUE_ADDRESS (msym); + data->known_tasks_length = 1; + + sym = lookup_symbol_in_language (KNOWN_TASKS_LIST, NULL, VAR_DOMAIN, + language_c, NULL); + 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; + } + + /* Can't find tasks. */ + + data->known_tasks_kind = ADA_TASKS_NOT_FOUND; + data->known_tasks_addr = 0; } -/* Read the known tasks from the inferior memory, and store it in - TASK_LIST. Return non-zero upon success. */ +/* Read the known tasks from the current inferior's memory, and store it + in the current inferior's data TASK_LIST. + Return non-zero upon success. */ static int read_known_tasks (void) { - /* In order to provide a fast response time, this function caches the - known tasks addresses after the lookup during the first call. */ - static CORE_ADDR known_tasks_array_addr; - static CORE_ADDR known_tasks_list_addr; + struct ada_tasks_inferior_data *data = + get_ada_tasks_inferior_data (current_inferior ()); /* Step 1: Clear the current list, if necessary. */ - VEC_truncate (ada_task_info_s, task_list, 0); + VEC_truncate (ada_task_info_s, data->task_list, 0); /* Step 2: do the real work. If the application does not use task, then no more needs to be done. @@ -699,153 +953,226 @@ read_known_tasks (void) 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 (ada_tasks_check_symbol_table) + ada_tasks_inferior_data_sniffer (data); + gdb_assert (data->known_tasks_kind != ADA_TASKS_UNKNOWN); + + switch (data->known_tasks_kind) { - known_tasks_array_addr = get_known_tasks_addr (KNOWN_TASKS_NAME); - known_tasks_list_addr = get_known_tasks_addr (KNOWN_TASKS_LIST); - - /* 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; + case ADA_TASKS_NOT_FOUND: /* Tasking not in use in inferior. */ + return 0; + case ADA_TASKS_ARRAY: + return read_known_tasks_array (data); + case ADA_TASKS_LIST: + return read_known_tasks_list (data); } - /* Try both mechanisms. */ - if ((known_tasks_array_addr == 0 - || read_known_tasks_array (known_tasks_array_addr) == 0) - && (known_tasks_list_addr == 0 - || read_known_tasks_list (known_tasks_list_addr) == 0)) - return 0; - - /* Step 3: Unset stale_task_list_p, to avoid re-reading the Known_Tasks + /* Step 3: Set task_list_valid_p, to avoid re-reading the Known_Tasks array unless needed. Then report a success. */ - stale_task_list_p = 0; + data->task_list_valid_p = 1; 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. */ +/* 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). */ int -ada_build_task_list (int warn_if_null) +ada_build_task_list (void) { + struct ada_tasks_inferior_data *data; + if (!target_has_stack) error (_("Cannot inspect Ada tasks when program is not running")); - if (stale_task_list_p) + data = get_ada_tasks_inferior_data (current_inferior ()); + if (!data->task_list_valid_p) read_known_tasks (); - if (task_list == NULL) - { - if (warn_if_null) - printf_filtered (_("Your application does not use any Ada tasks.\n")); - return 0; - } - - return 1; + return VEC_length (ada_task_info_s, data->task_list); } -/* Print a one-line description of the task whose number is TASKNO. - The formatting should fit the "info tasks" array. */ +/* 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 -short_task_info (int taskno) +void +print_ada_task_info (struct ui_out *uiout, + char *arg_str, + struct inferior *inf) { - 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); + struct ada_tasks_inferior_data *data; + int taskno, nb_tasks; + int taskno_arg = 0; + struct cleanup *old_chain; + int nb_columns; - /* Print a star if this task is the current task (or the task currently - selected). */ + if (ada_build_task_list () == 0) + { + ui_out_message (uiout, 0, + _("Your application does not use any Ada tasks.\n")); + return; + } - active_task_p = ptid_equal (task_info->ptid, inferior_ptid); - if (active_task_p) - printf_filtered ("*"); + if (arg_str != NULL && arg_str[0] != '\0') + taskno_arg = value_as_long (parse_and_eval (arg_str)); + + if (ui_out_is_mi_like_p (uiout)) + /* 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_find_new_threads (); + + 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 <= VEC_length (ada_task_info_s, data->task_list)) + nb_tasks = 1; + else + nb_tasks = 0; + } else - printf_filtered (" "); + nb_tasks = VEC_length (ada_task_info_s, data->task_list); + + nb_columns = ui_out_is_mi_like_p (uiout) ? 8 : 7; + old_chain = make_cleanup_ui_out_table_begin_end (uiout, nb_columns, + nb_tasks, "tasks"); + ui_out_table_header (uiout, 1, ui_left, "current", ""); + ui_out_table_header (uiout, 3, ui_right, "id", "ID"); + ui_out_table_header (uiout, 9, 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 (ui_out_is_mi_like_p (uiout)) + ui_out_table_header (uiout, 4, ui_right, "thread-id", ""); + ui_out_table_header (uiout, 4, ui_right, "parent-id", "P-ID"); + ui_out_table_header (uiout, 3, ui_right, "priority", "Pri"); + ui_out_table_header (uiout, 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. */ + ui_out_table_header (uiout, 1, ui_noalign, "name", "Name"); + ui_out_table_body (uiout); + + for (taskno = 1; + taskno <= VEC_length (ada_task_info_s, data->task_list); + taskno++) + { + const struct ada_task_info *const task_info = + VEC_index (ada_task_info_s, data->task_list, taskno - 1); + int parent_id; + struct cleanup *chain2; - /* Print the task number. */ - printf_filtered ("%3d", taskno); + gdb_assert (task_info != NULL); - /* Print the Task ID. */ - printf_filtered (" %9lx", (long) task_info->task_id); + /* 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; - /* Print the Task ID of the task parent. */ - printf_filtered (" %4d", get_task_number_from_id (task_info->parent)); + chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - /* Print the base priority of the task. */ - printf_filtered (" %3d", task_info->priority); + /* Print a star if this task is the current task (or the task + currently selected). */ + if (ptid_equal (task_info->ptid, inferior_ptid)) + ui_out_field_string (uiout, "current", "*"); + else + ui_out_field_skip (uiout, "current"); - /* 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 - printf_filtered (" %-22s", _(task_states[task_info->state])); + /* Print the task number. */ + ui_out_field_int (uiout, "id", taskno); - /* Finally, print the task name. */ - if (task_info->name[0] != '\0') - printf_filtered (" %s\n", task_info->name); - else - printf_filtered (_(" \n")); -} + /* Print the Task ID. */ + ui_out_field_fmt (uiout, "task-id", "%9lx", (long) task_info->task_id); + + /* Print the associated Thread ID. */ + if (ui_out_is_mi_like_p (uiout)) + { + const int thread_id = pid_to_thread_id (task_info->ptid); + + if (thread_id != 0) + ui_out_field_int (uiout, "thread-id", thread_id); + else + /* This should never happen unless there is a bug somewhere, + but be resilient when that happens. */ + ui_out_field_skip (uiout, "thread-id"); + } + + /* Print the ID of the parent task. */ + parent_id = get_task_number_from_id (task_info->parent, inf); + if (parent_id) + ui_out_field_int (uiout, "parent-id", parent_id); + else + ui_out_field_skip (uiout, "parent-id"); + + /* Print the base priority of the task. */ + ui_out_field_int (uiout, "priority", task_info->priority); + + /* Print the task current state. */ + if (task_info->caller_task) + ui_out_field_fmt (uiout, "state", + _("Accepting RV with %-4d"), + get_task_number_from_id (task_info->caller_task, + inf)); + else if (task_info->state == Entry_Caller_Sleep + && task_info->called_task) + ui_out_field_fmt (uiout, "state", + _("Waiting on RV with %-3d"), + get_task_number_from_id (task_info->called_task, + inf)); + else + ui_out_field_string (uiout, "state", task_states[task_info->state]); -/* Print a list containing a short description of all Ada tasks. */ -/* FIXME: Shouldn't we be using ui_out??? */ + /* Finally, print the task name. */ + ui_out_field_fmt (uiout, "name", + "%s", + task_info->name[0] != '\0' ? task_info->name + : _("")); -static void -info_tasks (int from_tty) -{ - int taskno; - const int nb_tasks = VEC_length (ada_task_info_s, task_list); + ui_out_text (uiout, "\n"); + do_cleanups (chain2); + } - printf_filtered (_(" ID TID P-ID Pri State Name\n")); - - for (taskno = 1; taskno <= nb_tasks; taskno++) - short_task_info (taskno); + do_cleanups (old_chain); } -/* 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, 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 (taskno <= 0 || taskno > VEC_length (ada_task_info_s, task_list)) + if (ada_build_task_list () == 0) + { + ui_out_message (uiout, 0, + _("Your application does not use any Ada tasks.\n")); + return; + } + + if (taskno <= 0 || taskno > VEC_length (ada_task_info_s, data->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); + task_info = VEC_index (ada_task_info_s, data->task_list, taskno - 1); /* Print the Ada task ID. */ printf_filtered (_("Ada Task: %s\n"), - paddress (target_gdbarch, task_info->task_id)); + paddress (target_gdbarch (), task_info->task_id)); /* Print the name of the task. */ if (task_info->name[0] != '\0') @@ -859,11 +1186,11 @@ info_task (char *taskno_str, int from_tty) /* 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); + VEC_index (ada_task_info_s, data->task_list, parent_taskno - 1); printf_filtered (_("Parent: %d"), parent_taskno); if (parent->name[0] != '\0') @@ -882,13 +1209,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) { - 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); } @@ -898,7 +1225,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); + VEC_index (ada_task_info_s, data->task_list, target_taskno - 1); if (target_task_info->name[0] != '\0') printf_filtered (" (%s)", target_task_info->name); @@ -917,15 +1244,12 @@ info_task (char *taskno_str, int from_tty) 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; + 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. @@ -946,15 +1270,16 @@ display_current_task_id (void) 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 (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 > VEC_length (ada_task_info_s, data->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); + task_info = VEC_index (ada_task_info_s, data->task_list, taskno - 1); if (!ada_task_is_alive (task_info)) error (_("Cannot switch to task %d: Task is no longer running"), taskno); @@ -985,7 +1310,8 @@ task_command_1 (char *taskno_str, int from_tty) 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); + frame_relative_level (get_selected_frame (NULL)), + SRC_AND_LOC, 1); } @@ -995,10 +1321,14 @@ task_command_1 (char *taskno_str, int from_tty) static void task_command (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) + { + ui_out_message (uiout, 0, + _("Your application does not use any Ada tasks.\n")); + return; + } if (taskno_str == NULL || taskno_str[0] == '\0') display_current_task_id (); @@ -1018,16 +1348,38 @@ task_command (char *taskno_str, int from_tty) 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 (struct inferior *inf) +{ + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); + + data->task_list_valid_p = 0; +} + +/* 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_task_list_changed (void) +ada_tasks_invalidate_inferior_data (struct inferior *inf) { - stale_task_list_p = 1; + struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf); + + data->known_tasks_kind = ADA_TASKS_UNKNOWN; + data->task_list_valid_p = 0; } /* The 'normal_stop' observer notification callback. */ @@ -1037,7 +1389,7 @@ ada_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. */ @@ -1045,14 +1397,35 @@ ada_normal_stop_observer (struct bpstats *unused_args, int unused_args2) static void ada_new_objfile_observer (struct objfile *objfile) { - /* Invalidate all cached data that were extracted from an objfile. */ + struct inferior *inf; + + /* Invalidate the relevant data in our program-space data. */ + + if (objfile == NULL) + { + /* All objfiles are being cleared, so we should clear all + our caches for all program spaces. */ + struct program_space *pspace; - atcb_type = NULL; - atcb_common_type = NULL; - atcb_ll_type = NULL; - atcb_call_type = NULL; + 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); + } + + /* 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. */ - ada_tasks_check_symbol_table = 1; + for (inf = inferior_list; inf != NULL; inf = inf->next) + if (objfile == NULL || inf->pspace == objfile->pspace) + ada_tasks_invalidate_inferior_data (inf); } /* Provide a prototype to silence -Wmissing-prototypes. */ @@ -1061,6 +1434,9 @@ extern initialize_file_ftype _initialize_tasks; void _initialize_tasks (void) { + ada_tasks_pspace_data_handle = register_program_space_data (); + ada_tasks_inferior_data_handle = register_inferior_data (); + /* Attach various observers. */ observer_attach_normal_stop (ada_normal_stop_observer); observer_attach_new_objfile (ada_new_objfile_observer);