aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Brobecker <brobecker@gnat.com>2011-07-04 19:32:07 +0000
committerJoel Brobecker <brobecker@gnat.com>2011-07-04 19:32:07 +0000
commit6040a59db5891821c1a73e88cd60adf125879a4f (patch)
tree116f9ae7cb1b302310791fce505accd2fc435a47
parentcb741e45a0ae3ab6009985b88b14e702919c690a (diff)
downloadgdb-6040a59db5891821c1a73e88cd60adf125879a4f.zip
gdb-6040a59db5891821c1a73e88cd60adf125879a4f.tar.gz
gdb-6040a59db5891821c1a73e88cd60adf125879a4f.tar.bz2
ada-tasks.c: handle known tasks maintained by a simply-linked list.
The mapping between Ada tasks, and the underlying threads is normally maintained by the GNAT runtime under the known_tasks array. For performance reasons, this array is just a static array with 10_000 entries in it. However, this is not very practical in certain environments where memory is limited. For those environments, the runtime has been enhanced to use an alternate scheme with a linked list. This change enhances the Ada tasking support to recognize this situation and use the correct way of reading the tasks info based on the the situation. gdb/ChangeLog (Tristan Gingold) * ada-tasks.c (KNOWN_TASKS_LIST): New macro. (tcb_fieldno): Add activation_link field. (get_known_tasks_addr): Moved and rewritten. (get_tcb_types_info): Set activation_link field. (read_known_tasks_array): Add parameter. Rewritten. (read_known_tasks_list): New function. (read_known_tasks): New function. (ada_build_task_list): Call read_known_tasks instead of read_known_tasks_array. * ravenscar-thread.c: Add first_task_name constant. (has_ravenscar_runtime): Check for task list too.
-rw-r--r--gdb/ChangeLog14
-rw-r--r--gdb/ada-tasks.c188
-rw-r--r--gdb/ravenscar-thread.c5
3 files changed, 142 insertions, 65 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e033f4a..7174f0d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,19 @@
2011-07-04 Tristan Gingold <gingold@adacore.com>
+ * ada-tasks.c (KNOWN_TASKS_LIST): New macro.
+ (tcb_fieldno): Add activation_link field.
+ (get_known_tasks_addr): Moved and rewritten.
+ (get_tcb_types_info): Set activation_link field.
+ (read_known_tasks_array): Add parameter. Rewritten.
+ (read_known_tasks_list): New function.
+ (read_known_tasks): New function.
+ (ada_build_task_list): Call read_known_tasks instead of
+ read_known_tasks_array.
+ * ravenscar-thread.c: Add first_task_name constant.
+ (has_ravenscar_runtime): Check for task list too.
+
+2011-07-04 Tristan Gingold <gingold@adacore.com>
+
* ada-tasks.c: Renames fieldno to actb_fieldno.
(ada_get_task_number): Indentation.
(get_tcb_types_info): Remove all parameters. Write directly
diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c
index 460a2fc..71fc25e 100644
--- a/gdb/ada-tasks.c
+++ b/gdb/ada-tasks.c
@@ -32,6 +32,11 @@
/* 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,
@@ -114,6 +119,7 @@ struct tcb_fieldnos
int priority;
int image;
int image_len; /* This field may be missing. */
+ int activation_link;
int call;
int ll;
@@ -299,54 +305,6 @@ read_fat_string_value (char *dest, struct value *val, int max_len)
dest[len] = '\0';
}
-/* Return the address of the Known_Tasks array maintained in
- the Ada Runtime. Return zero 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 minimal_symbol *msym;
-
- msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL);
- if (msym == NULL)
- return 0;
- known_tasks_addr = SYMBOL_VALUE_ADDRESS (msym);
-
- /* 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
@@ -435,6 +393,8 @@ get_tcb_types_info (void)
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.ll_thread = ada_get_field_index (ll_type, "thread", 0);
@@ -639,28 +599,16 @@ add_ada_task (CORE_ADDR task_id)
it in TASK_LIST. Return non-zero upon success. */
static int
-read_known_tasks_array (void)
+read_known_tasks_array (CORE_ADDR known_tasks_addr)
{
const int target_ptr_byte =
gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT;
- const CORE_ADDR known_tasks_addr = get_known_tasks_addr ();
const int known_tasks_size = target_ptr_byte * MAX_NUMBER_OF_KNOWN_TASKS;
gdb_byte *known_tasks = alloca (known_tasks_size);
int i;
- /* Step 1: Clear the current list, if necessary. */
- VEC_truncate (ada_task_info_s, task_list, 0);
-
- /* If the application does not use task, then no more needs to be done.
- It is important to have the task list cleared (see above) before we
- return, as we don't want a stale task list to be used... This can
- happen for instance when debugging a non-multitasking program after
- having debugged a multitasking one. */
- if (known_tasks_addr == 0)
- return 0;
-
- /* Step 2: Build a new list by reading the ATCBs from the Known_Tasks
- array in the Ada runtime. */
+ /* 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++)
{
@@ -674,6 +622,118 @@ read_known_tasks_array (void)
add_ada_task (task_id);
}
+ return 1;
+}
+
+/* Read the known tasks from the inferior memory, and store it in
+ TASK_LIST. Return non-zero upon success. */
+
+static int
+read_known_tasks_list (CORE_ADDR known_tasks_addr)
+{
+ const int target_ptr_byte =
+ gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT;
+ gdb_byte *known_tasks = alloca (target_ptr_byte);
+ struct type *data_ptr_type =
+ builtin_type (target_gdbarch)->builtin_data_ptr;
+ CORE_ADDR task_id;
+
+ /* Sanity check. */
+ if (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);
+ while (task_id != 0)
+ {
+ struct value *tcb_value;
+ struct value *common_value;
+
+ add_ada_task (task_id);
+
+ /* 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));
+ }
+
+ 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. */
+
+static CORE_ADDR
+get_known_tasks_addr (const char *name)
+{
+ struct minimal_symbol *msym;
+
+ msym = lookup_minimal_symbol (name, NULL, NULL);
+ if (msym == NULL)
+ return 0;
+
+ return SYMBOL_VALUE_ADDRESS (msym);
+}
+
+/* Read the known tasks from the inferior memory, and store it in
+ 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;
+
+ /* Step 1: Clear the current list, if necessary. */
+ VEC_truncate (ada_task_info_s, task_list, 0);
+
+ /* 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. */
+ if (ada_tasks_check_symbol_table)
+ {
+ 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;
+ }
+
+ /* 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
array unless needed. Then report a success. */
stale_task_list_p = 0;
@@ -692,7 +752,7 @@ ada_build_task_list (int warn_if_null)
error (_("Cannot inspect Ada tasks when program is not running"));
if (stale_task_list_p)
- read_known_tasks_array ();
+ read_known_tasks ();
if (task_list == NULL)
{
diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c
index 173a524..b324fe7 100644
--- a/gdb/ravenscar-thread.c
+++ b/gdb/ravenscar-thread.c
@@ -51,6 +51,7 @@ static ptid_t base_ptid;
static const char running_thread_name[] = "__gnat_running_thread_table";
static const char known_tasks_name[] = "system__tasking__debug__known_tasks";
+static const char first_task_name[] = "system__tasking__debug__first_task";
static const char ravenscar_runtime_initializer[] =
"system__bb__threads__initialize";
@@ -135,10 +136,12 @@ has_ravenscar_runtime (void)
lookup_minimal_symbol (ravenscar_runtime_initializer, NULL, NULL);
struct minimal_symbol *msym_known_tasks =
lookup_minimal_symbol (known_tasks_name, NULL, NULL);
+ struct minimal_symbol *msym_first_task =
+ lookup_minimal_symbol (first_task_name, NULL, NULL);
struct minimal_symbol *msym_running_thread = get_running_thread_msymbol ();
return (msym_ravenscar_runtime_initializer
- && msym_known_tasks
+ && (msym_known_tasks || msym_first_task)
&& msym_running_thread);
}