GDB (API)
|
00001 /* Copyright (C) 2008-2013 Free Software Foundation, Inc. 00002 00003 This file is part of GDB. 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 3 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 00017 00018 #include "defs.h" 00019 #include "command.h" 00020 #include "gdbcmd.h" 00021 #include "target.h" 00022 #include "observer.h" 00023 #include <sys/procfs.h> 00024 #include "gregset.h" 00025 #include "regcache.h" 00026 #include "inferior.h" 00027 #include "gdbthread.h" 00028 00029 #include <pthread_debug.h> 00030 00031 /* Print debugging traces if set to non-zero. */ 00032 static int debug_dec_thread = 0; 00033 00034 /* Non-zero if the dec-thread layer is active. */ 00035 static int dec_thread_active = 0; 00036 00037 /* The pthread_debug context. */ 00038 pthreadDebugContext_t debug_context; 00039 00040 /* The dec-thread target_ops structure. */ 00041 static struct target_ops dec_thread_ops; 00042 00043 /* Print a debug trace if DEBUG_DEC_THREAD is set (its value is adjusted 00044 by the user using "set debug dec-thread ..."). */ 00045 00046 static void 00047 debug (char *format, ...) 00048 { 00049 if (debug_dec_thread) 00050 { 00051 va_list args; 00052 00053 va_start (args, format); 00054 printf_unfiltered ("DEC Threads: "); 00055 vprintf_unfiltered (format, args); 00056 printf_unfiltered ("\n"); 00057 va_end (args); 00058 } 00059 } 00060 00061 /* pthread debug callbacks. */ 00062 00063 static int 00064 suspend_clbk (void *caller_context) 00065 { 00066 return ESUCCESS; 00067 } 00068 00069 static int 00070 resume_clbk (void *caller_context) 00071 { 00072 return ESUCCESS; 00073 } 00074 00075 static int 00076 hold_clbk (void *caller_context, pthreadDebugKId_t kernel_tid) 00077 { 00078 return ESUCCESS; 00079 } 00080 00081 static int 00082 unhold_clbk (void *caller_context, pthreadDebugKId_t kernel_tid) 00083 { 00084 return ESUCCESS; 00085 } 00086 00087 static int 00088 read_clbk (void *caller_context, void *address, void *buffer, 00089 unsigned long size) 00090 { 00091 int status = target_read_memory ((CORE_ADDR) address, buffer, size); 00092 00093 if (status != 0) 00094 return EINVAL; 00095 00096 return ESUCCESS; 00097 } 00098 00099 static int 00100 write_clbk (void *caller_context, void *address, void *buffer, 00101 unsigned long size) 00102 { 00103 int status = target_write_memory ((CORE_ADDR) address, buffer, size); 00104 00105 if (status != 0) 00106 return EINVAL; 00107 00108 return ESUCCESS; 00109 } 00110 00111 /* Get integer regs. */ 00112 00113 static int 00114 get_reg_clbk(void *caller_context, pthreadDebugGetRegRtn_t regs, 00115 pthreadDebugKId_t kernel_tid) 00116 { 00117 debug ("get_reg_clbk"); 00118 00119 /* Not sure that we actually need to do anything in this callback. */ 00120 return ESUCCESS; 00121 } 00122 00123 /* Set integer regs. */ 00124 00125 static int 00126 set_reg_clbk(void *caller_context, const pthreadDebugRegs_t *regs, 00127 pthreadDebugKId_t kernel_tid) 00128 { 00129 debug ("set_reg_clbk"); 00130 00131 /* Not sure that we actually need to do anything in this callback. */ 00132 return ESUCCESS; 00133 } 00134 00135 static int 00136 output_clbk (void *caller_context, char *line) 00137 { 00138 printf_filtered ("%s\n", line); 00139 return ESUCCESS; 00140 } 00141 00142 static int 00143 error_clbk (void *caller_context, char *line) 00144 { 00145 fprintf_filtered (gdb_stderr, "%s\n", line); 00146 return ESUCCESS; 00147 } 00148 00149 /* Get floating-point regs. */ 00150 00151 static int 00152 get_fpreg_clbk (void *caller_context, pthreadDebugFregs_p fregs, 00153 pthreadDebugKId_t kernel_tid) 00154 { 00155 debug ("get_fpreg_clbk"); 00156 00157 /* Not sure that we actually need to do anything in this callback. */ 00158 return ESUCCESS; 00159 } 00160 00161 /* Set floating-point regs. */ 00162 00163 static int 00164 set_fpreg_clbk (void *caller_context, const pthreadDebugFregs_t *fregs, 00165 pthreadDebugKId_t kernel_tid) 00166 { 00167 debug ("set_fpreg_clbk"); 00168 00169 /* Not sure that we actually need to do anything in this callback. */ 00170 return ESUCCESS; 00171 } 00172 00173 static void * 00174 malloc_clbk (void *caller_context, size_t size) 00175 { 00176 return xmalloc (size); 00177 } 00178 00179 static void 00180 free_clbk (void *caller_context, void *address) 00181 { 00182 xfree (address); 00183 } 00184 00185 static int 00186 kthdinfo_clbk (pthreadDebugClient_t caller_context, 00187 pthreadDebugKId_t kernel_tid, 00188 pthreadDebugKThreadInfo_p thread_info) 00189 { 00190 return ENOTSUP; 00191 } 00192 00193 static int 00194 speckthd_clbk (pthreadDebugClient_t caller_context, 00195 pthreadDebugSpecialType_t type, 00196 pthreadDebugKId_t *kernel_tid) 00197 { 00198 return ENOTSUP; 00199 } 00200 00201 static pthreadDebugCallbacks_t debug_callbacks = 00202 { 00203 PTHREAD_DEBUG_VERSION, 00204 (pthreadDebugGetMemRtn_t) read_clbk, 00205 (pthreadDebugSetMemRtn_t) write_clbk, 00206 suspend_clbk, 00207 resume_clbk, 00208 kthdinfo_clbk, 00209 hold_clbk, 00210 unhold_clbk, 00211 (pthreadDebugGetFregRtn_t) get_fpreg_clbk, 00212 (pthreadDebugSetFregRtn_t) set_fpreg_clbk, 00213 (pthreadDebugGetRegRtn_t) get_reg_clbk, 00214 (pthreadDebugSetRegRtn_t) set_reg_clbk, 00215 (pthreadDebugOutputRtn_t) output_clbk, 00216 (pthreadDebugOutputRtn_t) error_clbk, 00217 malloc_clbk, 00218 free_clbk, 00219 speckthd_clbk 00220 }; 00221 00222 /* Activate thread support if appropriate. Do nothing if thread 00223 support is already active. */ 00224 00225 static void 00226 enable_dec_thread (void) 00227 { 00228 struct minimal_symbol *msym; 00229 void* caller_context; 00230 int status; 00231 00232 /* If already active, nothing more to do. */ 00233 if (dec_thread_active) 00234 return; 00235 00236 msym = lookup_minimal_symbol ("__pthread_dbg_symtable", NULL, NULL); 00237 if (msym == NULL) 00238 { 00239 debug ("enable_dec_thread: No __pthread_dbg_symtable"); 00240 return; 00241 } 00242 00243 status = pthreadDebugContextInit (&caller_context, &debug_callbacks, 00244 (void *) SYMBOL_VALUE_ADDRESS (msym), 00245 &debug_context); 00246 if (status != ESUCCESS) 00247 { 00248 debug ("enable_dec_thread: pthreadDebugContextInit -> %d", 00249 status); 00250 return; 00251 } 00252 00253 push_target (&dec_thread_ops); 00254 dec_thread_active = 1; 00255 00256 debug ("enable_dec_thread: Thread support enabled."); 00257 } 00258 00259 /* Deactivate thread support. Do nothing if thread support is 00260 already inactive. */ 00261 00262 static void 00263 disable_dec_thread (void) 00264 { 00265 if (!dec_thread_active) 00266 return; 00267 00268 pthreadDebugContextDestroy (debug_context); 00269 unpush_target (&dec_thread_ops); 00270 dec_thread_active = 0; 00271 } 00272 00273 /* A structure that contains a thread ID and is associated 00274 pthreadDebugThreadInfo_t data. */ 00275 00276 struct dec_thread_info 00277 { 00278 pthreadDebugId_t thread; 00279 pthreadDebugThreadInfo_t info; 00280 }; 00281 typedef struct dec_thread_info dec_thread_info_s; 00282 00283 /* The list of user threads. */ 00284 00285 DEF_VEC_O (dec_thread_info_s); 00286 VEC(dec_thread_info_s) *dec_thread_list; 00287 00288 /* Release the memory used by the given VECP thread list pointer. 00289 Then set *VECP to NULL. */ 00290 00291 static void 00292 free_dec_thread_info_vec (VEC(dec_thread_info_s) **vecp) 00293 { 00294 int i; 00295 struct dec_thread_info *item; 00296 VEC(dec_thread_info_s) *vec = *vecp; 00297 00298 for (i = 0; VEC_iterate (dec_thread_info_s, vec, i, item); i++) 00299 xfree (item); 00300 VEC_free (dec_thread_info_s, vec); 00301 *vecp = NULL; 00302 } 00303 00304 /* Return a thread's ptid given its associated INFO. */ 00305 00306 static ptid_t 00307 ptid_build_from_info (struct dec_thread_info info) 00308 { 00309 int pid = ptid_get_pid (inferior_ptid); 00310 00311 return ptid_build (pid, 0, (long) info.thread); 00312 } 00313 00314 /* Return non-zero if PTID is still alive. 00315 00316 Assumes that DEC_THREAD_LIST is up to date. */ 00317 static int 00318 dec_thread_ptid_is_alive (ptid_t ptid) 00319 { 00320 pthreadDebugId_t tid = ptid_get_tid (ptid); 00321 int i; 00322 struct dec_thread_info *info; 00323 00324 if (tid == 0) 00325 /* This is the thread corresponding to the process. This ptid 00326 is always alive until the program exits. */ 00327 return 1; 00328 00329 /* Search whether an entry with the same tid exists in the dec-thread 00330 list of threads. If it does, then the thread is still alive. 00331 No match found means that the thread must be dead, now. */ 00332 for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); i++) 00333 if (info->thread == tid) 00334 return 1; 00335 return 0; 00336 } 00337 00338 /* Recompute the list of user threads and store the result in 00339 DEC_THREAD_LIST. */ 00340 00341 static void 00342 update_dec_thread_list (void) 00343 { 00344 pthreadDebugId_t thread; 00345 pthreadDebugThreadInfo_t info; 00346 int res; 00347 00348 free_dec_thread_info_vec (&dec_thread_list); 00349 res = pthreadDebugThdSeqInit (debug_context, &thread); 00350 while (res == ESUCCESS) 00351 { 00352 00353 res = pthreadDebugThdGetInfo (debug_context, thread, &info); 00354 if (res != ESUCCESS) 00355 warning (_("unable to get thread info, ignoring thread %ld"), 00356 thread); 00357 else if (info.kind == PTHREAD_DEBUG_THD_KIND_INITIAL 00358 || info.kind == PTHREAD_DEBUG_THD_KIND_NORMAL) 00359 { 00360 struct dec_thread_info *item = 00361 xmalloc (sizeof (struct dec_thread_info)); 00362 00363 item->thread = thread; 00364 item->info = info; 00365 VEC_safe_push (dec_thread_info_s, dec_thread_list, item); 00366 } 00367 res = pthreadDebugThdSeqNext (debug_context, &thread); 00368 } 00369 pthreadDebugThdSeqDestroy (debug_context); 00370 } 00371 00372 /* A callback to count the number of threads known to GDB. */ 00373 00374 static int 00375 dec_thread_count_gdb_threads (struct thread_info *ignored, void *context) 00376 { 00377 int *count = (int *) context; 00378 00379 *count = *count + 1; 00380 return 0; 00381 } 00382 00383 /* A callback that saves the given thread INFO at the end of an 00384 array. The end of the array is given in the CONTEXT and is 00385 incremented once the info has been added. */ 00386 00387 static int 00388 dec_thread_add_gdb_thread (struct thread_info *info, void *context) 00389 { 00390 struct thread_info ***listp = (struct thread_info ***) context; 00391 00392 **listp = info; 00393 *listp = *listp + 1; 00394 return 0; 00395 } 00396 00397 /* Implement the find_new_thread target_ops method. */ 00398 00399 static void 00400 dec_thread_find_new_threads (struct target_ops *ops) 00401 { 00402 int i; 00403 struct dec_thread_info *info; 00404 00405 update_dec_thread_list (); 00406 for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); i++) 00407 { 00408 ptid_t ptid = ptid_build_from_info (*info); 00409 00410 if (!in_thread_list (ptid)) 00411 add_thread (ptid); 00412 } 00413 } 00414 00415 /* Resynchronize the list of threads known by GDB with the actual 00416 list of threads reported by libpthread_debug. */ 00417 00418 static void 00419 resync_thread_list (struct target_ops *ops) 00420 { 00421 int i; 00422 int num_gdb_threads = 0; 00423 struct thread_info **gdb_thread_list; 00424 struct thread_info **next_thread_info; 00425 00426 /* Add new threads. */ 00427 dec_thread_find_new_threads (ops); 00428 00429 /* Remove threads that no longer exist. To help with the search, 00430 we build an array of GDB threads, and then iterate over this 00431 array. */ 00432 00433 iterate_over_threads (dec_thread_count_gdb_threads, 00434 (void *) &num_gdb_threads); 00435 gdb_thread_list = alloca (num_gdb_threads * sizeof (struct thread_info *)); 00436 next_thread_info = gdb_thread_list; 00437 iterate_over_threads (dec_thread_add_gdb_thread, (void *) &next_thread_info); 00438 00439 for (i = 0; i < num_gdb_threads; i++) 00440 if (!dec_thread_ptid_is_alive (gdb_thread_list[i]->ptid)) 00441 delete_thread (gdb_thread_list[i]->ptid); 00442 } 00443 00444 /* The "to_detach" method of the dec_thread_ops. */ 00445 00446 static void 00447 dec_thread_detach (struct target_ops *ops, char *args, int from_tty) 00448 { 00449 struct target_ops *beneath = find_target_beneath (ops); 00450 00451 debug ("dec_thread_detach"); 00452 00453 disable_dec_thread (); 00454 beneath->to_detach (beneath, args, from_tty); 00455 } 00456 00457 /* Return the ptid of the thread that is currently active. */ 00458 00459 static ptid_t 00460 get_active_ptid (void) 00461 { 00462 int i; 00463 struct dec_thread_info *info; 00464 00465 for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); 00466 i++) 00467 if (info->info.state == PTHREAD_DEBUG_STATE_RUNNING) 00468 return ptid_build_from_info (*info); 00469 00470 /* No active thread found. This can happen when the program 00471 has just exited. */ 00472 return null_ptid; 00473 } 00474 00475 /* The "to_wait" method of the dec_thread_ops. */ 00476 00477 static ptid_t 00478 dec_thread_wait (struct target_ops *ops, 00479 ptid_t ptid, struct target_waitstatus *status, int options) 00480 { 00481 ptid_t active_ptid; 00482 struct target_ops *beneath = find_target_beneath (ops); 00483 00484 debug ("dec_thread_wait"); 00485 00486 ptid = beneath->to_wait (beneath, ptid, status, options); 00487 00488 /* The ptid returned by the target beneath us is the ptid of the process. 00489 We need to find which thread is currently active and return its ptid. */ 00490 resync_thread_list (ops); 00491 active_ptid = get_active_ptid (); 00492 if (ptid_equal (active_ptid, null_ptid)) 00493 return ptid; 00494 return active_ptid; 00495 } 00496 00497 /* Fetch the general purpose and floating point registers for the given 00498 thread TID, and store the result in GREGSET and FPREGSET. Return 00499 zero if successful. */ 00500 00501 static int 00502 dec_thread_get_regsets (pthreadDebugId_t tid, gdb_gregset_t *gregset, 00503 gdb_fpregset_t *fpregset) 00504 { 00505 int res; 00506 pthreadDebugRegs_t regs; 00507 pthreadDebugFregs_t fregs; 00508 00509 res = pthreadDebugThdGetReg (debug_context, tid, ®s); 00510 if (res != ESUCCESS) 00511 { 00512 debug ("dec_thread_get_regsets: pthreadDebugThdGetReg -> %d", res); 00513 return -1; 00514 } 00515 memcpy (gregset->regs, ®s, sizeof (regs)); 00516 00517 res = pthreadDebugThdGetFreg (debug_context, tid, &fregs); 00518 if (res != ESUCCESS) 00519 { 00520 debug ("dec_thread_get_regsets: pthreadDebugThdGetFreg -> %d", res); 00521 return -1; 00522 } 00523 memcpy (fpregset->regs, &fregs, sizeof (fregs)); 00524 00525 return 0; 00526 } 00527 00528 /* The "to_fetch_registers" method of the dec_thread_ops. 00529 00530 Because the dec-thread debug API doesn't allow us to fetch 00531 only one register, we simply ignore regno and fetch+supply all 00532 registers. */ 00533 00534 static void 00535 dec_thread_fetch_registers (struct target_ops *ops, 00536 struct regcache *regcache, int regno) 00537 { 00538 pthreadDebugId_t tid = ptid_get_tid (inferior_ptid); 00539 gregset_t gregset; 00540 fpregset_t fpregset; 00541 int res; 00542 00543 debug ("dec_thread_fetch_registers (tid=%ld, regno=%d)", tid, regno); 00544 00545 00546 if (tid == 0 || ptid_equal (inferior_ptid, get_active_ptid ())) 00547 { 00548 struct target_ops *beneath = find_target_beneath (ops); 00549 00550 beneath->to_fetch_registers (beneath, regcache, regno); 00551 return; 00552 } 00553 00554 res = dec_thread_get_regsets (tid, &gregset, &fpregset); 00555 if (res != 0) 00556 return; 00557 00558 supply_gregset (regcache, &gregset); 00559 supply_fpregset (regcache, &fpregset); 00560 } 00561 00562 /* Store the registers given in GREGSET and FPREGSET into the associated 00563 general purpose and floating point registers of thread TID. Return 00564 zero if successful. */ 00565 00566 static int 00567 dec_thread_set_regsets (pthreadDebugId_t tid, gdb_gregset_t gregset, 00568 gdb_fpregset_t fpregset) 00569 { 00570 int res; 00571 pthreadDebugRegs_t regs; 00572 pthreadDebugFregs_t fregs; 00573 00574 memcpy (®s, gregset.regs, sizeof (regs)); 00575 res = pthreadDebugThdSetReg (debug_context, tid, ®s); 00576 if (res != ESUCCESS) 00577 { 00578 debug ("dec_thread_set_regsets: pthreadDebugThdSetReg -> %d", res); 00579 return -1; 00580 } 00581 00582 memcpy (&fregs, fpregset.regs, sizeof (fregs)); 00583 res = pthreadDebugThdSetFreg (debug_context, tid, &fregs); 00584 if (res != ESUCCESS) 00585 { 00586 debug ("dec_thread_set_regsets: pthreadDebugThdSetFreg -> %d", res); 00587 return -1; 00588 } 00589 00590 return 0; 00591 } 00592 00593 /* The "to_store_registers" method of the dec_thread_ops. 00594 00595 Because the dec-thread debug API doesn't allow us to store 00596 just one register, we store all the registers. */ 00597 00598 static void 00599 dec_thread_store_registers (struct target_ops *ops, 00600 struct regcache *regcache, int regno) 00601 { 00602 pthreadDebugId_t tid = ptid_get_tid (inferior_ptid); 00603 gregset_t gregset; 00604 fpregset_t fpregset; 00605 int res; 00606 00607 debug ("dec_thread_store_registers (tid=%ld, regno=%d)", tid, regno); 00608 00609 if (tid == 0 || ptid_equal (inferior_ptid, get_active_ptid ())) 00610 { 00611 struct target_ops *beneath = find_target_beneath (ops); 00612 00613 beneath->to_store_registers (beneath, regcache, regno); 00614 return; 00615 } 00616 00617 /* FIXME: brobecker/2008-05-28: I wonder if we could simply check 00618 in which register set the register is and then only store the 00619 registers for that register set, instead of storing both register 00620 sets. */ 00621 fill_gregset (regcache, &gregset, -1); 00622 fill_fpregset (regcache, &fpregset, -1); 00623 00624 res = dec_thread_set_regsets (tid, gregset, fpregset); 00625 if (res != 0) 00626 warning (_("failed to store registers.")); 00627 } 00628 00629 /* The "to_mourn_inferior" method of the dec_thread_ops. */ 00630 00631 static void 00632 dec_thread_mourn_inferior (struct target_ops *ops) 00633 { 00634 struct target_ops *beneath = find_target_beneath (ops); 00635 00636 debug ("dec_thread_mourn_inferior"); 00637 00638 disable_dec_thread (); 00639 beneath->to_mourn_inferior (beneath); 00640 } 00641 00642 /* The "to_thread_alive" method of the dec_thread_ops. */ 00643 static int 00644 dec_thread_thread_alive (struct target_ops *ops, ptid_t ptid) 00645 { 00646 debug ("dec_thread_thread_alive (tid=%ld)", ptid_get_tid (ptid)); 00647 00648 /* The thread list maintained by GDB is up to date, since we update 00649 it everytime we stop. So check this list. */ 00650 return in_thread_list (ptid); 00651 } 00652 00653 /* The "to_pid_to_str" method of the dec_thread_ops. */ 00654 00655 static char * 00656 dec_thread_pid_to_str (struct target_ops *ops, ptid_t ptid) 00657 { 00658 static char *ret = NULL; 00659 00660 if (ptid_get_tid (ptid) == 0) 00661 { 00662 struct target_ops *beneath = find_target_beneath (ops); 00663 00664 return beneath->to_pid_to_str (beneath, ptid); 00665 } 00666 00667 /* Free previous return value; a new one will be allocated by 00668 xstrprintf(). */ 00669 xfree (ret); 00670 00671 ret = xstrprintf (_("Thread %ld"), ptid_get_tid (ptid)); 00672 return ret; 00673 } 00674 00675 /* A "new-objfile" observer. Used to activate/deactivate dec-thread 00676 support. */ 00677 00678 static void 00679 dec_thread_new_objfile_observer (struct objfile *objfile) 00680 { 00681 if (objfile != NULL) 00682 enable_dec_thread (); 00683 else 00684 disable_dec_thread (); 00685 } 00686 00687 /* The "to_get_ada_task_ptid" method of the dec_thread_ops. */ 00688 00689 static ptid_t 00690 dec_thread_get_ada_task_ptid (long lwp, long thread) 00691 { 00692 int i; 00693 struct dec_thread_info *info; 00694 00695 debug ("dec_thread_get_ada_task_ptid (lwp=0x%lx, thread=0x%lx)", 00696 lwp, thread); 00697 00698 for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); 00699 i++) 00700 if (info->info.teb == (pthread_t) thread) 00701 return ptid_build_from_info (*info); 00702 00703 warning (_("Could not find thread id from THREAD = 0x%lx"), thread); 00704 return inferior_ptid; 00705 } 00706 00707 static void 00708 init_dec_thread_ops (void) 00709 { 00710 dec_thread_ops.to_shortname = "dec-threads"; 00711 dec_thread_ops.to_longname = _("DEC threads support"); 00712 dec_thread_ops.to_doc = _("DEC threads support"); 00713 dec_thread_ops.to_detach = dec_thread_detach; 00714 dec_thread_ops.to_wait = dec_thread_wait; 00715 dec_thread_ops.to_fetch_registers = dec_thread_fetch_registers; 00716 dec_thread_ops.to_store_registers = dec_thread_store_registers; 00717 dec_thread_ops.to_mourn_inferior = dec_thread_mourn_inferior; 00718 dec_thread_ops.to_thread_alive = dec_thread_thread_alive; 00719 dec_thread_ops.to_find_new_threads = dec_thread_find_new_threads; 00720 dec_thread_ops.to_pid_to_str = dec_thread_pid_to_str; 00721 dec_thread_ops.to_stratum = thread_stratum; 00722 dec_thread_ops.to_get_ada_task_ptid = dec_thread_get_ada_task_ptid; 00723 dec_thread_ops.to_magic = OPS_MAGIC; 00724 } 00725 00726 void 00727 _initialize_dec_thread (void) 00728 { 00729 init_dec_thread_ops (); 00730 complete_target_initialization (&dec_thread_ops); 00731 00732 observer_attach_new_objfile (dec_thread_new_objfile_observer); 00733 00734 add_setshow_boolean_cmd ("dec-thread", class_maintenance, &debug_dec_thread, 00735 _("Set debugging of DEC threads module."), 00736 _("Show debugging of DEC threads module."), 00737 _("Enables debugging output (used to debug GDB)."), 00738 NULL, NULL, 00739 &setdebuglist, &showdebuglist); 00740 }