Commit | Line | Data |
---|---|---|
14f9c5c9 AS |
1 | /* file ada-tasks.c: Ada tasking control for GDB |
2 | Copyright 1997 Free Software Foundation, Inc. | |
3 | Contributed by Ada Core Technologies, Inc | |
4 | . | |
5 | This file is part of GDB. | |
6 | ||
7 | [$Id$] | |
8 | Authors: Roch-Alexandre Nomine Beguin, Arnaud Charlet <charlet@gnat.com> | |
9 | ||
10 | This program is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2 of the License, or | |
13 | (at your option) any later version. | |
14 | ||
15 | */ | |
16 | ||
d2e4a39e AS |
17 | #include <ctype.h> |
18 | #include "defs.h" | |
19 | #include "command.h" | |
14f9c5c9 AS |
20 | #include "value.h" |
21 | #include "language.h" | |
22 | #include "inferior.h" | |
23 | #include "symtab.h" | |
24 | #include "target.h" | |
7cb47b14 | 25 | #include "regcache.h" |
14f9c5c9 AS |
26 | #include "gdbcore.h" |
27 | ||
28 | #if (defined(__alpha__) && defined(__osf__) && !defined(__alpha_vxworks)) | |
29 | #include <sys/procfs.h> | |
30 | #endif | |
31 | ||
32 | #if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)) | |
33 | #include "gregset.h" | |
d2e4a39e | 34 | #endif |
14f9c5c9 AS |
35 | |
36 | #include "ada-lang.h" | |
37 | ||
38 | /* FIXME: move all this conditional compilation in description | |
39 | files or in configure.in */ | |
40 | ||
41 | #if defined (VXWORKS_TARGET) | |
42 | #define THREAD_TO_PID(tid,lwpid) (tid) | |
43 | ||
44 | #elif defined (linux) | |
45 | #define THREAD_TO_PID(tid,lwpid) (0) | |
46 | ||
47 | #elif (defined (sun) && defined (__SVR4)) | |
48 | #define THREAD_TO_PID thread_to_pid | |
49 | ||
50 | #elif defined (sgi) || defined (__WIN32__) || defined (hpux) | |
51 | #define THREAD_TO_PID(tid,lwpid) ((int)lwpid) | |
52 | ||
53 | #else | |
54 | #define THREAD_TO_PID(tid,lwpid) (0) | |
55 | #endif | |
56 | ||
57 | #if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET) | |
58 | #define THREAD_FETCH_REGISTERS dec_thread_fetch_registers | |
59 | #define GET_CURRENT_THREAD dec_thread_get_current_thread | |
60 | extern int dec_thread_get_registers (gdb_gregset_t *, gdb_fpregset_t *); | |
61 | #endif | |
62 | ||
63 | #if defined (_AIX) | |
64 | #define THREAD_FETCH_REGISTERS aix_thread_fetch_registers | |
65 | #define GET_CURRENT_THREAD aix_thread_get_current_thread | |
66 | #endif | |
67 | ||
68 | #if defined(VXWORKS_TARGET) | |
69 | #define GET_CURRENT_THREAD() ((void*)inferior_pid) | |
70 | #define THREAD_FETCH_REGISTERS() (-1) | |
71 | ||
72 | #elif defined (sun) && defined (__SVR4) | |
73 | #define GET_CURRENT_THREAD solaris_thread_get_current_thread | |
74 | #define THREAD_FETCH_REGISTERS() (-1) | |
d2e4a39e | 75 | extern void *GET_CURRENT_THREAD (); |
14f9c5c9 AS |
76 | |
77 | #elif defined (_AIX) || (defined(__alpha__) && defined(__osf__)) | |
d2e4a39e | 78 | extern void *GET_CURRENT_THREAD (); |
14f9c5c9 AS |
79 | |
80 | #elif defined (__WIN32__) || defined (hpux) | |
81 | #define GET_CURRENT_THREAD() (inferior_pid) | |
82 | #define THREAD_FETCH_REGISTERS() (-1) | |
83 | ||
84 | #else | |
85 | #define GET_CURRENT_THREAD() (NULL) | |
86 | #define THREAD_FETCH_REGISTERS() (-1) | |
87 | #endif | |
88 | ||
89 | #define KNOWN_TASKS_NAME "system__tasking__debug__known_tasks" | |
90 | ||
91 | #define READ_MEMORY(addr, var) read_memory (addr, (char*) &var, sizeof (var)) | |
92 | /* external declarations */ | |
93 | ||
14f9c5c9 AS |
94 | /* Global visible variables */ |
95 | ||
96 | struct task_entry *task_list = NULL; | |
97 | int ada__tasks_check_symbol_table = 1; | |
98 | void *pthread_kern_addr = NULL; | |
99 | ||
100 | #if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)) | |
101 | gdb_gregset_t gregset_saved; | |
102 | gdb_fpregset_t fpregset_saved; | |
103 | #endif | |
104 | ||
105 | /* The maximum number of tasks known to the Ada runtime */ | |
106 | const int MAX_NUMBER_OF_KNOWN_TASKS = 1000; | |
107 | ||
108 | /* the current task */ | |
109 | int current_task = -1, current_task_id = -1, current_task_index; | |
110 | void *current_thread, *current_lwp; | |
111 | ||
d2e4a39e | 112 | char *ada_task_states[] = { |
14f9c5c9 AS |
113 | "Unactivated", |
114 | "Runnable", | |
115 | "Terminated", | |
116 | "Child Activation Wait", | |
117 | "Accept Statement", | |
118 | "Waiting on entry call", | |
119 | "Async Select Wait", | |
120 | "Delay Sleep", | |
121 | "Child Termination Wait", | |
122 | "Wait Child in Term Alt", | |
123 | "", | |
124 | "", | |
125 | "", | |
126 | "", | |
127 | "Asynchronous Hold" | |
128 | }; | |
129 | ||
130 | /* Global internal types */ | |
131 | ||
d2e4a39e | 132 | static char *ada_long_task_states[] = { |
14f9c5c9 AS |
133 | "Unactivated", |
134 | "Runnable", | |
135 | "Terminated", | |
136 | "Waiting for child activation", | |
137 | "Blocked in accept statement", | |
138 | "Waiting on entry call", | |
139 | "Asynchronous Selective Wait", | |
140 | "Delay Sleep", | |
141 | "Waiting for children termination", | |
142 | "Waiting for children in terminate alternative", | |
143 | "", | |
144 | "", | |
145 | "", | |
146 | "", | |
147 | "Asynchronous Hold" | |
148 | }; | |
149 | ||
150 | /* Global internal variables */ | |
151 | ||
152 | static int highest_task_num = 0; | |
d2e4a39e | 153 | int thread_support = 0; /* 1 if the thread library in use is supported */ |
14f9c5c9 AS |
154 | static int gdbtk_task_initialization = 0; |
155 | ||
d2e4a39e | 156 | static int |
80ae6ee2 | 157 | add_task_entry (void *p_task_id, int index) |
14f9c5c9 AS |
158 | { |
159 | struct task_entry *new_task_entry = NULL; | |
160 | struct task_entry *pt; | |
161 | ||
162 | highest_task_num++; | |
aacb1f0a | 163 | new_task_entry = xmalloc (sizeof (struct task_entry)); |
14f9c5c9 AS |
164 | new_task_entry->task_num = highest_task_num; |
165 | new_task_entry->task_id = p_task_id; | |
166 | new_task_entry->known_tasks_index = index; | |
167 | new_task_entry->next_task = NULL; | |
168 | pt = task_list; | |
169 | if (pt) | |
170 | { | |
171 | while (pt->next_task) | |
172 | pt = pt->next_task; | |
173 | pt->next_task = new_task_entry; | |
174 | pt->stack_per = 0; | |
175 | } | |
d2e4a39e AS |
176 | else |
177 | task_list = new_task_entry; | |
14f9c5c9 AS |
178 | return new_task_entry->task_num; |
179 | } | |
180 | ||
d2e4a39e | 181 | int |
80ae6ee2 | 182 | get_entry_number (void *p_task_id) |
14f9c5c9 AS |
183 | { |
184 | struct task_entry *pt; | |
185 | ||
186 | pt = task_list; | |
187 | while (pt != NULL) | |
188 | { | |
189 | if (pt->task_id == p_task_id) | |
190 | return pt->task_num; | |
191 | pt = pt->next_task; | |
192 | } | |
193 | return 0; | |
194 | } | |
195 | ||
80ae6ee2 AS |
196 | static struct task_entry * |
197 | get_thread_entry_vptr (void *thread) | |
14f9c5c9 AS |
198 | { |
199 | struct task_entry *pt; | |
200 | ||
201 | pt = task_list; | |
202 | while (pt != NULL) | |
203 | { | |
204 | if (pt->thread == thread) | |
d2e4a39e | 205 | return pt; |
14f9c5c9 AS |
206 | pt = pt->next_task; |
207 | } | |
208 | return 0; | |
209 | } | |
210 | ||
80ae6ee2 AS |
211 | static struct task_entry * |
212 | get_entry_vptr (int p_task_num) | |
14f9c5c9 AS |
213 | { |
214 | struct task_entry *pt; | |
215 | ||
216 | pt = task_list; | |
217 | while (pt) | |
218 | { | |
219 | if (pt->task_num == p_task_num) | |
220 | return pt; | |
221 | pt = pt->next_task; | |
222 | } | |
223 | return NULL; | |
224 | } | |
225 | ||
80ae6ee2 AS |
226 | void |
227 | init_task_list (void) | |
14f9c5c9 AS |
228 | { |
229 | struct task_entry *pt, *old_pt; | |
230 | ||
231 | pt = task_list; | |
232 | while (pt) | |
233 | { | |
234 | old_pt = pt; | |
235 | pt = pt->next_task; | |
aacb1f0a | 236 | xfree (old_pt); |
14f9c5c9 AS |
237 | }; |
238 | task_list = NULL; | |
239 | highest_task_num = 0; | |
240 | } | |
241 | ||
80ae6ee2 AS |
242 | int |
243 | valid_task_id (int task) | |
14f9c5c9 AS |
244 | { |
245 | return get_entry_vptr (task) != NULL; | |
246 | } | |
247 | ||
80ae6ee2 AS |
248 | void * |
249 | get_self_id (void) | |
14f9c5c9 | 250 | { |
d2e4a39e | 251 | struct value *val; |
14f9c5c9 AS |
252 | void *self_id; |
253 | int result; | |
254 | struct task_entry *ent; | |
255 | extern int do_not_insert_breakpoints; | |
256 | ||
257 | #if !((defined(sun) && defined(__SVR4)) || defined(VXWORKS_TARGET) || defined(__WIN32__)) | |
258 | if (thread_support) | |
259 | #endif | |
260 | { | |
261 | ent = get_thread_entry_vptr (GET_CURRENT_THREAD ()); | |
262 | return ent ? ent->task_id : 0; | |
263 | } | |
264 | ||
265 | /* FIXME: calling a function in the inferior with a multithreaded application | |
266 | is not reliable, so return NULL if there is no safe way to get the current | |
267 | task */ | |
268 | return NULL; | |
269 | } | |
270 | ||
d2e4a39e | 271 | int |
4dc81987 | 272 | get_current_task (void) |
14f9c5c9 AS |
273 | { |
274 | int result; | |
d2e4a39e | 275 | |
14f9c5c9 AS |
276 | /* FIXME: language_ada should be defined in defs.h */ |
277 | /* if (current_language->la_language != language_ada) return -1; */ | |
278 | ||
279 | result = get_entry_number (get_self_id ()); | |
280 | ||
281 | /* return -1 if not found */ | |
282 | return result == 0 ? -1 : result; | |
283 | } | |
284 | ||
285 | /* Print detailed information about specified task */ | |
286 | ||
287 | static void | |
80ae6ee2 | 288 | info_task (char *arg, int from_tty) |
14f9c5c9 AS |
289 | { |
290 | void *temp_task; | |
291 | struct task_entry *pt, *pt2; | |
292 | void *self_id, *caller; | |
293 | struct task_fields atcb, atcb2; | |
294 | struct entry_call call; | |
d2e4a39e AS |
295 | int bounds[2]; |
296 | char image[256]; | |
14f9c5c9 AS |
297 | int num; |
298 | ||
299 | /* FIXME: language_ada should be defined in defs.h */ | |
300 | /* if (current_language->la_language != language_ada) | |
d2e4a39e AS |
301 | { |
302 | printf_filtered ("The current language does not support tasks.\n"); | |
303 | return; | |
304 | } | |
305 | */ | |
14f9c5c9 AS |
306 | pt = get_entry_vptr (atoi (arg)); |
307 | if (pt == NULL) | |
308 | { | |
d2e4a39e AS |
309 | printf_filtered ("Task %s not found.\n", arg); |
310 | return; | |
14f9c5c9 AS |
311 | } |
312 | ||
313 | temp_task = pt->task_id; | |
314 | ||
315 | /* read the atcb in the inferior */ | |
316 | READ_MEMORY ((CORE_ADDR) temp_task, atcb); | |
317 | ||
318 | /* print the Ada task id */ | |
319 | printf_filtered ("Ada Task: %p\n", temp_task); | |
320 | ||
321 | /* print the name of the task */ | |
d2e4a39e AS |
322 | if (atcb.image.P_ARRAY != NULL) |
323 | { | |
324 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_BOUNDS), bounds); | |
325 | bounds[1] = EXTRACT_INT (bounds[1]); | |
326 | read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_ARRAY), | |
327 | (char *) &image, bounds[1]); | |
328 | printf_filtered ("Name: %.*s\n", bounds[1], image); | |
329 | } | |
330 | else | |
331 | printf_filtered ("<no name>\n"); | |
14f9c5c9 AS |
332 | |
333 | /* print the thread id */ | |
334 | ||
335 | if ((long) pt->thread < 65536) | |
336 | printf_filtered ("Thread: %ld\n", (long int) pt->thread); | |
337 | else | |
338 | printf_filtered ("Thread: %p\n", pt->thread); | |
339 | ||
340 | if ((long) pt->lwp != 0) | |
341 | { | |
342 | if ((long) pt->lwp < 65536) | |
d2e4a39e | 343 | printf_filtered ("LWP: %ld\n", (long int) pt->lwp); |
14f9c5c9 | 344 | else |
d2e4a39e | 345 | printf_filtered ("LWP: %p\n", pt->lwp); |
14f9c5c9 AS |
346 | } |
347 | ||
348 | /* print the parent gdb task id */ | |
349 | num = get_entry_number (EXTRACT_ADDRESS (atcb.parent)); | |
350 | if (num != 0) | |
351 | { | |
352 | printf_filtered ("Parent: %d", num); | |
353 | pt2 = get_entry_vptr (num); | |
354 | READ_MEMORY ((CORE_ADDR) pt2->task_id, atcb2); | |
355 | ||
356 | /* print the name of the task */ | |
d2e4a39e AS |
357 | if (atcb2.image.P_ARRAY != NULL) |
358 | { | |
359 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_BOUNDS), | |
360 | bounds); | |
361 | bounds[1] = EXTRACT_INT (bounds[1]); | |
362 | read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_ARRAY), | |
363 | (char *) &image, bounds[1]); | |
364 | printf_filtered (" (%.*s)\n", bounds[1], image); | |
365 | } | |
14f9c5c9 | 366 | else |
d2e4a39e | 367 | printf_filtered ("\n"); |
14f9c5c9 AS |
368 | } |
369 | else | |
370 | printf_filtered ("No parent\n"); | |
371 | ||
372 | /* print the base priority of the task */ | |
373 | printf_filtered ("Base Priority: %d\n", EXTRACT_INT (atcb.priority)); | |
374 | ||
375 | /* print the current state of the task */ | |
376 | ||
377 | /* check if this task is accepting a rendezvous */ | |
378 | if (atcb.call == NULL) | |
379 | caller = NULL; | |
d2e4a39e AS |
380 | else |
381 | { | |
382 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.call), call); | |
383 | caller = EXTRACT_ADDRESS (call.self); | |
384 | } | |
385 | ||
14f9c5c9 AS |
386 | if (caller != NULL) |
387 | { | |
388 | num = get_entry_number (caller); | |
389 | printf_filtered ("Accepting rendezvous with %d", num); | |
390 | ||
391 | if (num != 0) | |
392 | { | |
393 | pt2 = get_entry_vptr (num); | |
394 | READ_MEMORY ((CORE_ADDR) pt2->task_id, atcb2); | |
395 | ||
396 | /* print the name of the task */ | |
d2e4a39e AS |
397 | if (atcb2.image.P_ARRAY != NULL) |
398 | { | |
399 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_BOUNDS), | |
400 | bounds); | |
401 | bounds[1] = EXTRACT_INT (bounds[1]); | |
402 | read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb2.image.P_ARRAY), | |
403 | (char *) &image, bounds[1]); | |
404 | printf_filtered (" (%.*s)\n", bounds[1], image); | |
405 | } | |
14f9c5c9 AS |
406 | else |
407 | printf_filtered ("\n"); | |
408 | } | |
409 | else | |
410 | printf_filtered ("\n"); | |
411 | } | |
412 | else | |
d2e4a39e | 413 | printf_filtered ("State: %s\n", ada_long_task_states[atcb.state]); |
14f9c5c9 AS |
414 | } |
415 | ||
416 | #if 0 | |
417 | ||
418 | /* A useful function that shows the alignment of all the fields in the | |
419 | tasks_fields structure | |
420 | */ | |
421 | ||
80ae6ee2 | 422 | print_align (void) |
14f9c5c9 AS |
423 | { |
424 | struct task_fields tf; | |
d2e4a39e AS |
425 | void *tf_base = &(tf); |
426 | void *tf_state = &(tf.state); | |
427 | void *tf_entry_num = &(tf.entry_num); | |
428 | void *tf_parent = &(tf.parent); | |
429 | void *tf_priority = &(tf.priority); | |
14f9c5c9 | 430 | void *tf_current_priority = &(tf.current_priority); |
d2e4a39e AS |
431 | void *tf_image = &(tf.image); |
432 | void *tf_call = &(tf.call); | |
433 | void *tf_thread = &(tf.thread); | |
434 | void *tf_lwp = &(tf.lwp); | |
14f9c5c9 AS |
435 | printf_filtered ("\n"); |
436 | printf_filtered ("(tf_base = 0x%x)\n", tf_base); | |
d2e4a39e AS |
437 | printf_filtered ("task_fields.entry_num at %3d (0x%x)\n", |
438 | tf_entry_num - tf_base, tf_entry_num); | |
439 | printf_filtered ("task_fields.state at %3d (0x%x)\n", | |
440 | tf_state - tf_base, tf_state); | |
441 | printf_filtered ("task_fields.parent at %3d (0x%x)\n", | |
442 | tf_parent - tf_base, tf_parent); | |
443 | printf_filtered ("task_fields.priority at %3d (0x%x)\n", | |
444 | tf_priority - tf_base, tf_priority); | |
445 | printf_filtered ("task_fields.current_priority at %3d (0x%x)\n", | |
446 | tf_current_priority - tf_base, tf_current_priority); | |
447 | printf_filtered ("task_fields.image at %3d (0x%x)\n", | |
448 | tf_image - tf_base, tf_image); | |
449 | printf_filtered ("task_fields.call at %3d (0x%x)\n", | |
450 | tf_call - tf_base, tf_call); | |
451 | printf_filtered ("task_fields.thread at %3d (0x%x)\n", | |
452 | tf_thread - tf_base, tf_thread); | |
453 | printf_filtered ("task_fields.lwp at %3d (0x%x)\n", | |
454 | tf_lwp - tf_base, tf_lwp); | |
455 | printf_filtered ("\n"); | |
14f9c5c9 AS |
456 | } |
457 | #endif | |
458 | ||
459 | /* Print information about currently known tasks */ | |
460 | ||
461 | static void | |
80ae6ee2 | 462 | info_tasks (char *arg, int from_tty) |
14f9c5c9 | 463 | { |
d2e4a39e | 464 | struct value *val; |
14f9c5c9 | 465 | int i, task_number, state; |
d2e4a39e | 466 | void *temp_task, *temp_tasks[MAX_NUMBER_OF_KNOWN_TASKS]; |
14f9c5c9 | 467 | struct task_entry *pt; |
d2e4a39e | 468 | void *self_id, *caller, *thread_id = NULL; |
14f9c5c9 AS |
469 | struct task_fields atcb; |
470 | struct entry_call call; | |
d2e4a39e AS |
471 | int bounds[2]; |
472 | char image[256]; | |
14f9c5c9 AS |
473 | int size; |
474 | char car; | |
475 | ||
476 | #if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET) | |
477 | pthreadTeb_t thr; | |
478 | gdb_gregset_t regs; | |
479 | #endif | |
480 | ||
481 | static struct symbol *sym; | |
482 | static struct minimal_symbol *msym; | |
483 | static void *known_tasks_addr = NULL; | |
484 | ||
485 | int init_only = gdbtk_task_initialization; | |
486 | gdbtk_task_initialization = 0; | |
487 | ||
488 | task_number = 0; | |
489 | ||
d2e4a39e | 490 | if (PIDGET (inferior_ptid) == 0) |
14f9c5c9 AS |
491 | { |
492 | printf_filtered ("The program is not being run under gdb. "); | |
493 | printf_filtered ("Use 'run' or 'attach' first.\n"); | |
494 | return; | |
495 | } | |
496 | ||
497 | if (ada__tasks_check_symbol_table) | |
498 | { | |
499 | thread_support = 0; | |
500 | #if (defined(__alpha__) && defined(__osf__) & !defined(VXWORKS_TARGET)) || \ | |
501 | defined (_AIX) | |
502 | thread_support = 1; | |
503 | #endif | |
504 | ||
505 | msym = lookup_minimal_symbol (KNOWN_TASKS_NAME, NULL, NULL); | |
506 | if (msym != NULL) | |
507 | known_tasks_addr = (void *) SYMBOL_VALUE_ADDRESS (msym); | |
508 | else | |
509 | #ifndef VXWORKS_TARGET | |
d2e4a39e | 510 | return; |
14f9c5c9 AS |
511 | #else |
512 | { | |
513 | if (target_lookup_symbol (KNOWN_TASKS_NAME, &known_tasks_addr) != 0) | |
514 | return; | |
515 | } | |
516 | #endif | |
517 | ||
518 | ada__tasks_check_symbol_table = 0; | |
519 | } | |
520 | ||
521 | if (known_tasks_addr == NULL) | |
522 | return; | |
523 | ||
524 | #if !((defined(sun) && defined(__SVR4)) || defined(VXWORKS_TARGET) || defined(__WIN32__) || defined (hpux)) | |
525 | if (thread_support) | |
526 | #endif | |
527 | thread_id = GET_CURRENT_THREAD (); | |
528 | ||
529 | /* then we get a list of tasks created */ | |
530 | ||
531 | init_task_list (); | |
532 | ||
533 | READ_MEMORY ((CORE_ADDR) known_tasks_addr, temp_tasks); | |
534 | ||
d2e4a39e | 535 | for (i = 0; i < MAX_NUMBER_OF_KNOWN_TASKS; i++) |
14f9c5c9 AS |
536 | { |
537 | temp_task = EXTRACT_ADDRESS (temp_tasks[i]); | |
538 | ||
539 | if (temp_task != NULL) | |
d2e4a39e AS |
540 | { |
541 | task_number = get_entry_number (temp_task); | |
542 | if (task_number == 0) | |
14f9c5c9 | 543 | task_number = add_task_entry (temp_task, i); |
d2e4a39e AS |
544 | } |
545 | } | |
14f9c5c9 AS |
546 | |
547 | /* Return without printing anything if this function was called in | |
548 | order to init GDBTK tasking. */ | |
549 | ||
d2e4a39e AS |
550 | if (init_only) |
551 | return; | |
14f9c5c9 AS |
552 | |
553 | /* print the header */ | |
554 | ||
555 | #if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET) | |
556 | printf_filtered | |
d2e4a39e | 557 | (" ID TID P-ID Pri Stack %% State Name\n"); |
14f9c5c9 AS |
558 | #else |
559 | printf_filtered (" ID TID P-ID Pri State Name\n"); | |
560 | #endif | |
561 | ||
562 | /* Now that we have a list of task id's, we can print them */ | |
563 | pt = task_list; | |
564 | while (pt) | |
565 | { | |
566 | temp_task = pt->task_id; | |
567 | ||
568 | /* read the atcb in the inferior */ | |
569 | READ_MEMORY ((CORE_ADDR) temp_task, atcb); | |
570 | ||
571 | /* store the thread id for future use */ | |
572 | pt->thread = EXTRACT_ADDRESS (atcb.thread); | |
573 | ||
574 | #if defined (linux) | |
575 | pt->lwp = (void *) THREAD_TO_PID (atcb.thread, 0); | |
576 | #else | |
577 | pt->lwp = EXTRACT_ADDRESS (atcb.lwp); | |
578 | #endif | |
579 | ||
580 | /* print a star if this task is the current one */ | |
581 | if (thread_id) | |
582 | #if defined (__WIN32__) || defined (SGI) || defined (hpux) | |
583 | printf_filtered (pt->lwp == thread_id ? "*" : " "); | |
584 | #else | |
585 | printf_filtered (pt->thread == thread_id ? "*" : " "); | |
586 | #endif | |
587 | ||
588 | /* print the gdb task id */ | |
589 | printf_filtered ("%3d", pt->task_num); | |
590 | ||
591 | /* print the Ada task id */ | |
592 | #ifndef VXWORKS_TARGET | |
593 | printf_filtered (" %9lx", (long) temp_task); | |
594 | #else | |
595 | #ifdef TARGET_64 | |
d2e4a39e | 596 | printf_filtered (" %#9lx", (unsigned long) pt->thread & 0x3ffffffffff); |
14f9c5c9 | 597 | #else |
d2e4a39e | 598 | printf_filtered (" %#9lx", (long) pt->thread); |
14f9c5c9 AS |
599 | #endif |
600 | #endif | |
601 | ||
602 | /* print the parent gdb task id */ | |
603 | printf_filtered | |
d2e4a39e | 604 | (" %4d", get_entry_number (EXTRACT_ADDRESS (atcb.parent))); |
14f9c5c9 AS |
605 | |
606 | /* print the base priority of the task */ | |
607 | printf_filtered (" %3d", EXTRACT_INT (atcb.priority)); | |
608 | ||
609 | #if defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET) | |
610 | if (pt->task_num == 1 || atcb.state == Terminated) | |
611 | { | |
d2e4a39e | 612 | printf_filtered (" Unknown"); |
14f9c5c9 AS |
613 | goto next; |
614 | } | |
615 | ||
d2e4a39e | 616 | read_memory ((CORE_ADDR) atcb.thread, &thr, sizeof (thr)); |
14f9c5c9 | 617 | current_thread = atcb.thread; |
d2e4a39e AS |
618 | regs.regs[SP_REGNUM] = 0; |
619 | if (dec_thread_get_registers (®s, NULL) == 0) | |
620 | { | |
621 | pt->stack_per = (100 * ((long) thr.__stack_base - | |
622 | regs.regs[SP_REGNUM])) / thr.__stack_size; | |
623 | /* if the thread is terminated but still there, the | |
624 | stack_base/size values are erroneous. Try to patch it */ | |
625 | if (pt->stack_per < 0 || pt->stack_per > 100) | |
626 | pt->stack_per = 0; | |
627 | } | |
14f9c5c9 AS |
628 | |
629 | /* print information about stack space used in the thread */ | |
d2e4a39e | 630 | if (thr.__stack_size < 1024 * 1024) |
14f9c5c9 AS |
631 | { |
632 | size = thr.__stack_size / 1024; | |
633 | car = 'K'; | |
634 | } | |
d2e4a39e | 635 | else if (thr.__stack_size < 1024 * 1024 * 1024) |
14f9c5c9 AS |
636 | { |
637 | size = thr.__stack_size / 1024 / 1024; | |
638 | car = 'M'; | |
639 | } | |
d2e4a39e | 640 | else /* Who knows... */ |
14f9c5c9 AS |
641 | { |
642 | size = thr.__stack_size / 1024 / 1024 / 1024; | |
643 | car = 'G'; | |
644 | } | |
645 | printf_filtered (" %4d%c %2d", size, car, pt->stack_per); | |
d2e4a39e | 646 | next: |
14f9c5c9 AS |
647 | #endif |
648 | ||
649 | /* print the current state of the task */ | |
650 | ||
651 | /* check if this task is accepting a rendezvous */ | |
652 | if (atcb.call == NULL) | |
653 | caller = NULL; | |
d2e4a39e AS |
654 | else |
655 | { | |
656 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.call), call); | |
657 | caller = EXTRACT_ADDRESS (call.self); | |
658 | } | |
659 | ||
14f9c5c9 | 660 | if (caller != NULL) |
d2e4a39e AS |
661 | printf_filtered (" Accepting RV with %-4d", |
662 | get_entry_number (caller)); | |
14f9c5c9 AS |
663 | else |
664 | { | |
665 | state = atcb.state; | |
666 | #if defined (__WIN32__) || defined (SGI) || defined (hpux) | |
667 | if (state == Runnable && (thread_id && pt->lwp == thread_id)) | |
668 | #else | |
669 | if (state == Runnable && (thread_id && pt->thread == thread_id)) | |
670 | #endif | |
671 | /* Replace "Runnable" by "Running" if this is the current task */ | |
672 | printf_filtered (" %-22s", "Running"); | |
673 | else | |
d2e4a39e | 674 | printf_filtered (" %-22s", ada_task_states[state]); |
14f9c5c9 AS |
675 | } |
676 | ||
677 | /* finally, print the name of the task */ | |
d2e4a39e AS |
678 | if (atcb.image.P_ARRAY != NULL) |
679 | { | |
680 | READ_MEMORY ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_BOUNDS), | |
681 | bounds); | |
682 | bounds[1] = EXTRACT_INT (bounds[1]); | |
683 | read_memory ((CORE_ADDR) EXTRACT_ADDRESS (atcb.image.P_ARRAY), | |
684 | (char *) &image, bounds[1]); | |
685 | printf_filtered (" %.*s\n", bounds[1], image); | |
686 | } | |
687 | else | |
688 | printf_filtered (" <no name>\n"); | |
14f9c5c9 AS |
689 | |
690 | pt = pt->next_task; | |
691 | } | |
692 | } | |
693 | ||
694 | /* Task list initialization for GDB-Tk. We basically use info_tasks() | |
695 | to initialize our variables, but abort that function before we | |
696 | actually print anything. */ | |
697 | ||
698 | int | |
80ae6ee2 | 699 | gdbtk_tcl_tasks_initialize (void) |
14f9c5c9 AS |
700 | { |
701 | gdbtk_task_initialization = 1; | |
702 | info_tasks ("", gdb_stdout); | |
703 | ||
704 | return (task_list != NULL); | |
705 | } | |
706 | ||
707 | static void | |
80ae6ee2 | 708 | info_tasks_command (char *arg, int from_tty) |
14f9c5c9 | 709 | { |
d2e4a39e AS |
710 | if (arg == NULL || *arg == '\000') |
711 | info_tasks (arg, from_tty); | |
712 | else | |
713 | info_task (arg, from_tty); | |
14f9c5c9 AS |
714 | } |
715 | ||
716 | /* Switch from one thread to another. */ | |
717 | ||
718 | static void | |
719 | switch_to_thread (ptid_t ptid) | |
14f9c5c9 AS |
720 | { |
721 | if (ptid_equal (ptid, inferior_ptid)) | |
722 | return; | |
723 | ||
724 | inferior_ptid = ptid; | |
725 | flush_cached_frames (); | |
726 | registers_changed (); | |
727 | stop_pc = read_pc (); | |
728 | select_frame (get_current_frame ()); | |
729 | } | |
730 | ||
731 | /* Switch to a specified task. */ | |
732 | ||
80ae6ee2 AS |
733 | static int |
734 | task_switch (void *tid, void *lwpid) | |
14f9c5c9 AS |
735 | { |
736 | int res = 0, pid; | |
737 | ||
738 | if (thread_support) | |
739 | { | |
740 | flush_cached_frames (); | |
741 | ||
742 | if (current_task != current_task_id) | |
743 | { | |
744 | res = THREAD_FETCH_REGISTERS (); | |
745 | } | |
746 | else | |
747 | { | |
748 | #if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)) | |
749 | supply_gregset (&gregset_saved); | |
750 | supply_fpregset (&fpregset_saved); | |
751 | #endif | |
752 | } | |
753 | ||
d2e4a39e AS |
754 | if (res == 0) |
755 | stop_pc = read_pc (); | |
14f9c5c9 AS |
756 | select_frame (get_current_frame ()); |
757 | return res; | |
758 | } | |
759 | ||
760 | return -1; | |
761 | } | |
762 | ||
80ae6ee2 AS |
763 | static void |
764 | task_command (char *tidstr, int from_tty) | |
14f9c5c9 AS |
765 | { |
766 | int num; | |
767 | struct task_entry *e; | |
768 | ||
769 | if (!tidstr) | |
770 | error ("Please specify a task ID. Use the \"info tasks\" command to\n" | |
d2e4a39e | 771 | "see the IDs of currently known tasks."); |
14f9c5c9 AS |
772 | |
773 | num = atoi (tidstr); | |
774 | e = get_entry_vptr (num); | |
775 | ||
776 | if (e == NULL) | |
777 | error ("Task ID %d not known. Use the \"info tasks\" command to\n" | |
d2e4a39e | 778 | "see the IDs of currently known tasks.", num); |
14f9c5c9 AS |
779 | |
780 | if (current_task_id == -1) | |
781 | { | |
782 | #if (defined(__alpha__) && defined(__osf__) && !defined(VXWORKS_TARGET)) | |
783 | fill_gregset (&gregset_saved, -1); | |
784 | fill_fpregset (&fpregset_saved, -1); | |
785 | #endif | |
786 | current_task_id = get_current_task (); | |
787 | } | |
788 | ||
789 | current_task = num; | |
790 | current_task_index = e->known_tasks_index; | |
791 | current_thread = e->thread; | |
792 | current_lwp = e->lwp; | |
793 | if (task_switch (e->thread, e->lwp) == 0) | |
794 | { | |
795 | /* FIXME: find_printable_frame should be defined in frame.h, and | |
d2e4a39e | 796 | implemented in ada-lang.c */ |
6e7f8b9c | 797 | /* find_printable_frame (deprecated_selected_frame, frame_relative_level (deprecated_selected_frame)); */ |
14f9c5c9 | 798 | printf_filtered ("[Switching to task %d]\n", num); |
6e7f8b9c AC |
799 | print_stack_frame (deprecated_selected_frame, |
800 | frame_relative_level (deprecated_selected_frame), 1); | |
14f9c5c9 AS |
801 | } |
802 | else | |
803 | printf_filtered ("Unable to switch to task %d\n", num); | |
804 | } | |
805 | ||
806 | void | |
80ae6ee2 | 807 | _initialize_tasks (void) |
14f9c5c9 AS |
808 | { |
809 | static struct cmd_list_element *task_cmd_list = NULL; | |
810 | extern struct cmd_list_element *cmdlist; | |
811 | ||
d2e4a39e AS |
812 | add_info ("tasks", info_tasks_command, |
813 | "Without argument: list all known Ada tasks, with status information.\n" | |
814 | "info tasks n: print detailed information of task n.\n"); | |
14f9c5c9 AS |
815 | |
816 | add_prefix_cmd ("task", class_run, task_command, | |
d2e4a39e AS |
817 | "Use this command to switch between tasks.\n\ |
818 | The new task ID must be currently known.", &task_cmd_list, "task ", 1, &cmdlist); | |
14f9c5c9 | 819 | } |