1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
|
/* Native support for the SGI Iris running IRIX version 5, for GDB.
Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Contributed by Alessandro Forin(af@cs.cmu.edu) at CMU
and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
Implemented for Irix 4.x by Garrett A. Wollman.
Modified for Irix 5.x by Ian Lance Taylor.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "defs.h"
#include "inferior.h"
#include "gdbcore.h"
#include "target.h"
#include "regcache.h"
#include "gdb_string.h"
#include <sys/time.h>
#include <sys/procfs.h>
#include <setjmp.h> /* For JB_XXX. */
/* Prototypes for supply_gregset etc. */
#include "gregset.h"
#ifdef HAVE_SPYTHREAD
#include "irix6-spyThread.h"
#include <poll.h>
#include <sys/errno.h>
/* Auxiliary information attached to each spyProc_t */
typedef struct {
int nthreads; /* Number thread_status slots used. */
int max_nthreads; /* Size of thread_status array */
prstatus_t* thread_status;
int num_initial_uthreads; /* Number of kernel threads when attached */
prstatus_t* initial_uthread_status;
/* Initial kernel thread statuses */
} thread_data_t;
/* Auxiliary information for spyProc_t* HANDLE. */
#define THREAD_INFO(handle) ((thread_data_t*) ((handle)->sp_client))
/* True if IRIX 6 pthreads appear to be present. */
static int libspy_present = 0;
/* True if the spythread library has been enabled. */
int libspy_enabled = 0;
/* Vector of spyThread operations, initialized by initialize_libspy. */
spyThreadCalls_t spy_ops;
#endif
static void fetch_core_registers (char *, unsigned int, int, CORE_ADDR);
/* Size of elements in jmpbuf */
#define JB_ELEMENT_SIZE 4
/*
* See the comment in m68k-tdep.c regarding the utility of these functions.
*
* These definitions are from the MIPS SVR4 ABI, so they may work for
* any MIPS SVR4 target.
*/
void
supply_gregset (gregset_t *gregsetp)
{
register int regi;
register greg_t *regp = &(*gregsetp)[0];
int gregoff = sizeof (greg_t) - MIPS_REGSIZE;
static char zerobuf[32] = {0};
for (regi = 0; regi <= CTX_RA; regi++)
supply_register (regi, (char *) (regp + regi) + gregoff);
supply_register (PC_REGNUM, (char *) (regp + CTX_EPC) + gregoff);
supply_register (HI_REGNUM, (char *) (regp + CTX_MDHI) + gregoff);
supply_register (LO_REGNUM, (char *) (regp + CTX_MDLO) + gregoff);
supply_register (CAUSE_REGNUM, (char *) (regp + CTX_CAUSE) + gregoff);
/* Fill inaccessible registers with zero. */
supply_register (BADVADDR_REGNUM, zerobuf);
}
void
fill_gregset (gregset_t *gregsetp, int regno)
{
int regi;
register greg_t *regp = &(*gregsetp)[0];
/* Under Irix6, if GDB is built with N32 ABI and is debugging an O32
executable, we have to sign extend the registers to 64 bits before
filling in the gregset structure. */
for (regi = 0; regi <= CTX_RA; regi++)
if ((regno == -1) || (regno == regi))
*(regp + regi) =
extract_signed_integer (®isters[REGISTER_BYTE (regi)],
REGISTER_RAW_SIZE (regi));
if ((regno == -1) || (regno == PC_REGNUM))
*(regp + CTX_EPC) =
extract_signed_integer (®isters[REGISTER_BYTE (PC_REGNUM)],
REGISTER_RAW_SIZE (PC_REGNUM));
if ((regno == -1) || (regno == CAUSE_REGNUM))
*(regp + CTX_CAUSE) =
extract_signed_integer (®isters[REGISTER_BYTE (CAUSE_REGNUM)],
REGISTER_RAW_SIZE (CAUSE_REGNUM));
if ((regno == -1) || (regno == HI_REGNUM))
*(regp + CTX_MDHI) =
extract_signed_integer (®isters[REGISTER_BYTE (HI_REGNUM)],
REGISTER_RAW_SIZE (HI_REGNUM));
if ((regno == -1) || (regno == LO_REGNUM))
*(regp + CTX_MDLO) =
extract_signed_integer (®isters[REGISTER_BYTE (LO_REGNUM)],
REGISTER_RAW_SIZE (LO_REGNUM));
}
/*
* Now we do the same thing for floating-point registers.
* We don't bother to condition on FP0_REGNUM since any
* reasonable MIPS configuration has an R3010 in it.
*
* Again, see the comments in m68k-tdep.c.
*/
void
supply_fpregset (fpregset_t *fpregsetp)
{
register int regi;
static char zerobuf[32] = {0};
/* FIXME, this is wrong for the N32 ABI which has 64 bit FP regs. */
for (regi = 0; regi < 32; regi++)
supply_register (FP0_REGNUM + regi,
(char *) &fpregsetp->fp_r.fp_regs[regi]);
supply_register (FCRCS_REGNUM, (char *) &fpregsetp->fp_csr);
/* FIXME: how can we supply FCRIR_REGNUM? SGI doesn't tell us. */
supply_register (FCRIR_REGNUM, zerobuf);
}
void
fill_fpregset (fpregset_t *fpregsetp, int regno)
{
int regi;
char *from, *to;
/* FIXME, this is wrong for the N32 ABI which has 64 bit FP regs. */
for (regi = FP0_REGNUM; regi < FP0_REGNUM + 32; regi++)
{
if ((regno == -1) || (regno == regi))
{
from = (char *) ®isters[REGISTER_BYTE (regi)];
to = (char *) &(fpregsetp->fp_r.fp_regs[regi - FP0_REGNUM]);
memcpy (to, from, REGISTER_RAW_SIZE (regi));
}
}
if ((regno == -1) || (regno == FCRCS_REGNUM))
fpregsetp->fp_csr = *(unsigned *) ®isters[REGISTER_BYTE (FCRCS_REGNUM)];
}
/* Figure out where the longjmp will land.
We expect the first arg to be a pointer to the jmp_buf structure from which
we extract the pc (JB_PC) that we will land at. The pc is copied into PC.
This routine returns true on success. */
int
get_longjmp_target (CORE_ADDR *pc)
{
char *buf;
CORE_ADDR jb_addr;
buf = alloca (TARGET_PTR_BIT / TARGET_CHAR_BIT);
jb_addr = read_register (A0_REGNUM);
if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
TARGET_PTR_BIT / TARGET_CHAR_BIT))
return 0;
*pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
return 1;
}
/* Provide registers to GDB from a core file.
CORE_REG_SECT points to an array of bytes, which were obtained from
a core file which BFD thinks might contain register contents.
CORE_REG_SIZE is its size.
Normally, WHICH says which register set corelow suspects this is:
0 --- the general-purpose register set
2 --- the floating-point register set
However, for Irix 5, WHICH isn't used.
REG_ADDR is also unused. */
static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
int which, CORE_ADDR reg_addr)
{
if (core_reg_size == REGISTER_BYTES)
{
memcpy ((char *) registers, core_reg_sect, core_reg_size);
}
else if (MIPS_REGSIZE == 4 &&
core_reg_size == (2 * MIPS_REGSIZE) * NUM_REGS)
{
/* This is a core file from a N32 executable, 64 bits are saved
for all registers. */
char *srcp = core_reg_sect;
char *dstp = registers;
int regno;
for (regno = 0; regno < NUM_REGS; regno++)
{
if (regno >= FP0_REGNUM && regno < (FP0_REGNUM + 32))
{
/* FIXME, this is wrong, N32 has 64 bit FP regs, but GDB
currently assumes that they are 32 bit. */
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
if (REGISTER_RAW_SIZE (regno) == 4)
{
/* copying 4 bytes from eight bytes?
I don't see how this can be right... */
srcp += 4;
}
else
{
/* copy all 8 bytes (sizeof(double)) */
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
}
}
else
{
srcp += 4;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
*dstp++ = *srcp++;
}
}
}
else
{
warning ("wrong size gregset struct in core file");
return;
}
registers_fetched ();
}
/* IRIX 6.5 pthread support */
#ifdef HAVE_SPYTHREAD
/* The callback structure used to give the IRIX spyThread module
access to the program's symbol table and (when applicable) the
core file. */
static int spy_symbol_addr (void* data, char* name, off64_t* result);
static int spy_memory_read (void*, int, off64_t, char*, size_t);
static int spy_register_read (void*, int, void*);
static spyCallBack_t spy_callbacks = {
spy_symbol_addr, 0,
spy_memory_read, 0,
spy_register_read, 0
};
/* Arguments passed to callback for resuming kernel threads. */
typedef struct {
prrun_t* prrun_p;
int num_resumed;
} resume_info_t;
#endif
/* General Notes on IRIX pthreads.
1. On IRIX 6.5, there are both kernel threads (uthreads) and pthreads
that run on top of them. There are numerous complications, most
importantly:
+ When a pthread is not associated with a uthread, the system
interface only allows fetching the registers of the thread: there
is no other status, no pending signals, etc.
+ The status returned for the process itself contains a mixture of
flags that apply to the process as a whole plus flags from what
is essentially a *random* uthread---one that does not necessarily
correspond to the "current thread", and is not necessarily stopped
on an event of interest! Therefore, to find a useful status for
the process, we must find the uthreads and synthesize a more
useful status, containing the "interesting" flags from the
current thread, plus the process-specific flags.
+ It is possible for there to be uthreads without associated
pthreads, and the current thread can be a pure uthread. We
report such threads as distinct threads identified by their
uthread (lwpid). Since a pthread can move from uthread to
uthread, this results in some uthreads appearing and disappearing
repeatedly (as that uthread is associated with one pthread or
another and then becomes idle, and unattached to any pthread).
This is less than ideal, but the alternatives seem to be either
seeing no trace of the "unassigned" uthreads, or seeing two
threads (one a uthread, one a pthread) that really represent the
same thread.
2. Debugging Irix 6 pthreads requires use of a dynamic library,
libpthread.so, which the program will dynamically load if it uses
pthreads. We test for its presence by looking for the symbol
__pt_lib_info -- a kludgy technique, perhaps, but taken directly
from Workshop.
3. We usurp certain fields of the prstatus structures (apparently
previously unused by GDB on IRIX) for our purposes. Specifically,
pr_nthreads: For the main process's status: total number of threads
pr_who (== pr_un.pr_st.who): For the main process's status:
current thread id. For a kernel thread: the kernel
thread number.
*/
/*
LOCAL FUNCTION
initialize_libspy -- test for the presence of the pthread debugging library.
SYNOPSIS
static void initialize_libspy ()
DESCRIPTION
Called after each shared-library load, this routine sets the
variable libspy_present according to whether an indicative symbol
is defined.
*/
void
initialize_libspy (void)
{
#if defined (HAVE_SPYTHREAD)
libspy_present =
lookup_symbol ("__pt_lib_info", NULL, VAR_NAMESPACE, 0, NULL) != NULL;
if (! libspy_present)
libspy_enabled = 0;
else if (!libspy_enabled)
{
spyThreadInit (&spy_ops, NULL);
libspy_enabled = 1;
}
#endif
}
#ifdef HAVE_SPYTHREAD
/* The next three functions are callbacks used by the spyThread
library to access the symbol table and core image. I believe that
core-image access should be unnecessary. */
/*
LOCAL FUNCTION
spy_symbol_addr -- Get address of symbol
SYNOPSIS
static int spy_symbol_addr (void* dummy, char* name, off64_t* result)
DESCRIPTION
Store address of external symbol NAME in *RESULT.
*/
static int
spy_symbol_addr (void* dummy, char* name, off64_t* result)
{
struct symbol* sym = lookup_symbol (name, NULL, VAR_NAMESPACE, 0, NULL);
if (sym == NULL) {
return -1;
}
if (SYMBOL_CLASS (sym) == LOC_BLOCK)
*result = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
else if (SYMBOL_CLASS (sym) == LOC_STATIC)
*result = SYMBOL_VALUE_ADDRESS (sym);
else
{
warning ("spy_symbol_addr: %s has wrong class", name);
return -1;
}
return 0;
}
static int
spy_memory_read (void* a0, int threadnum, off64_t vaddr, char* outbuf,
size_t len)
{
warning ("spy_memory_read called unexpectedly");
return -1;
}
static int
spy_register_read (void* a0, int threadnum, void* outbuf)
{
warning ("spy_register_read called unexpectedly");
return -1;
}
/*
GLOBAL FUNCTION
irix_spy_open -- Initialize spyThread handle to a process
SYNOPSIS
int irix_spy_open (int fd, spyProc_t** handle_p)
DESCRIPTION
Set *HANDLE_P to point to a spy-thread library handle for the process
at with file descriptor FD, if the pthread facilities have been
enabled. Otherwise, set to NULL. Returns 1 on success, 0 if
thread library not enabled.
*/
int
irix_spy_open (int fd, spyProc_t** handle_p)
{
spyProc_t* handle;
thread_data_t* thread_info;
prstatus_t* kernel_threads;
int nthreads;
if (! libspy_enabled)
return 0;
if (*handle_p != NULL)
return 1;
handle = *handle_p = (spyProc_t*) xmalloc (sizeof (spyProc_t));
handle->sp_abi = SP_N32; /* FIXME */
handle->sp_procfd = fd;
handle->sp_client = thread_info =
(thread_data_t*) xmalloc (sizeof (thread_data_t));
handle->sp_vec = &spy_callbacks;
thread_info->nthreads = thread_info->max_nthreads = 0;
thread_info->thread_status = NULL;
spy_ops.stc_ProcNew (*handle_p);
nthreads = thread_info->num_initial_uthreads
= irix_get_kernel_threads (*handle_p, &kernel_threads);
if (nthreads > 0)
{
thread_info->initial_uthread_status =
(prstatus_t*) xmalloc (nthreads * sizeof (prstatus_t));
memcpy (thread_info->initial_uthread_status, kernel_threads,
nthreads * sizeof (prstatus_t));
}
else
thread_info->initial_uthread_status = NULL;
return 1;
}
/*
GLOBAL FUNCTION
irix_spy_close --
SYNOPSIS
void irix_spy_close (spyProc_t* handle)
DESCRIPTION
Release resources associated with HANDLE. Does nothing if
HANDLE is null.
*/
void
irix_spy_close (spyProc_t* handle)
{
if (handle != NULL)
{
xfree (THREAD_INFO (handle)->thread_status);
xfree (THREAD_INFO (handle)->initial_uthread_status);
xfree (THREAD_INFO (handle));
spy_ops.stc_ProcDel (handle);
}
xfree (handle);
}
/*
LOCAL FUNCTION
thread_scan -- Operate on a pthread, or on all pthreads
SYNOPSIS
static int thread_scan (spyProc_t* handle, int op, uint_t domain,
spyThread_t thread, scan_callback_t f,
void* result_buffer, void* data)
DESCRIPTION
Interface to the stc_ScanOp operation. Here, we describe only the
operations used.
Perform OP on selected pthreads under the process accessed by HANDLE.
OP may be one of the PIOC* values for ioctl (see /proc). For a thread
with no kernel context, on PIOCGREG and PIOCGFPREG work. OP may also
be SPYCGNAME, which is essentially a no-op useful when scanning
multiple threads. For the PIOC operations, RESULT_BUFFER is the
expected buffer operand to ioctl.
Thread selection depends on value of DOMAIN:
+ STC_THREAD: operate only on THREAD (a kernel thread number
or user thread number)
+ STC_SCAN_ALL: operate on all threads (THREAD ignored)
+ STC_SCAN_KERN: operate on all kernel threads (THREAD ignored).
If non-null, F is called for each selected thread as follows:
F(HANDLE, t, RESULT_BUFFER, DATA)
where 't' is THREAD (for STC_THREAD), or the kernel thread number,
if present, or (for a thread without kernel context), the pthread number.
When the OP is SPYCGNAME, RESULT_BUFFER should be a pointer to a
spyThread_t, which will be set to the pthread number (user thread
id), if present, and otherwise to the kernel thread number (for
kernel threads without user context). An STC_SCAN operation terminates
when the callback returns non-zero.
When called with STC_THREAD, returns the value of the operation
selected. Otherwise, returns 0 (OK) or the error code.
*/
static int
thread_scan (spyProc_t* handle,
int op, uint_t domain, spyThread_t thread, scan_callback_t f,
void* result_buffer, void* data)
{
spyScanOp_t args;
args.sso_dom = domain;
args.sso_thd = thread;
args.sso_cb = f;
args.sso_cbArg = data;
return spy_ops.stc_ScanOp (handle, op, result_buffer, &args);
}
static int
get_thread_callback (spyProc_t* handle, spyThread_t uthread_id,
void* pthread_idp, void* uthread_idp)
{
*((spyThread_t*) uthread_idp) = uthread_id;
return 0;
}
/*
LOCAL FUNCTION
get_kernel_thread -- find the kernel thread running a given pthread.
SYNOPSIS
static spyThread_t get_kernel_thread (spyProc_t* handle, spyThread_t id)
DESCRIPTION
Return the kernel thread number running ID on the process accessed
by HANDLE, if any. Simply returns ID if there is none.
*/
static spyThread_t
get_kernel_thread (spyProc_t* handle, spyThread_t id)
{
spyThread_t uthread, pthread;
if (handle == NULL)
return id;
thread_scan (handle, SPYCGNAME, STC_THREAD, id, get_thread_callback,
&pthread, &uthread);
return uthread;
}
static int
collect_pthread_callback (spyProc_t* handle, spyThread_t uthread_id,
void* pthread_idp0, void* unused)
{
int i;
thread_data_t* info = THREAD_INFO (handle);
spyThread_t* pthread_idp = (spyThread_t*) pthread_idp0;
if (info->nthreads >= info->max_nthreads)
{
info->max_nthreads = info->max_nthreads == 0 ? 8 : 2*info->max_nthreads;
info->thread_status =
(prstatus_t*) xrealloc (info->thread_status,
info->max_nthreads * sizeof (prstatus_t));
}
if (IS_LWP_ID (uthread_id))
{
if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread_id, NULL,
&info->thread_status[info->nthreads], NULL) < 0)
warning ("irix_get_threads: cannot get status for thread %x",
uthread_id);
}
else
{
if (thread_scan (handle, PIOCGREG, STC_THREAD, uthread_id, NULL,
&info->thread_status[info->nthreads].pr_reg, NULL) < 0)
warning ("irix_get_threads: cannot get registers for thread %x",
uthread_id);
info->thread_status[info->nthreads].pr_flags =
PR_STOPPED | PR_ISTOP | PR_RETURN;
info->thread_status[info->nthreads].pr_why = PR_REQUESTED;
info->thread_status[info->nthreads].pr_what = 0;
}
info->thread_status[info->nthreads].pr_who = *pthread_idp;
info->nthreads += 1;
return 0;
}
static int
collect_uthread_callback (spyProc_t* handle, spyThread_t uthread_id,
void* pthread_idp0, void* unused)
{
int code =
collect_pthread_callback (handle, uthread_id, pthread_idp0, NULL);
if (code == 0)
{
thread_data_t* info = THREAD_INFO (handle);
info->thread_status[info->nthreads-1].pr_who = uthread_id;
}
return code;
}
/*
GLOBAL FUNCTION
irix_get_threads -- Collect status information on all threads
SYNOPSIS
int irix_get_threads (spyProc_t* handle, prstatus_t** thread_list)
DESCRIPTION
Set *THREAD_LIST to a pointer to a vector of status blocks for all
threads running under the process accessed by HANDLE. Returns the
number of threads.
*/
int
irix_get_threads (spyProc_t* handle, prstatus_t** thread_list)
{
if (handle != NULL)
{
spyThread_t buffer;
THREAD_INFO (handle)->nthreads = 0;
thread_scan (handle, SPYCGNAME, STC_SCAN_ALL, 0,
collect_pthread_callback, &buffer, NULL);
*thread_list = THREAD_INFO(handle)->thread_status;
return THREAD_INFO(handle)->nthreads;
}
*thread_list = NULL;
return 0;
}
/*
GLOBAL FUNCTION
irix_get_kernel_threads -- Collect status information on kernel threads.
SYNOPSIS
int irix_get_kernel_threads (spyProc_t* handle, prstatus_t** thread_list)
DESCRIPTION
Set *THREAD_LIST to a pointer to a vector of status blocks for all
threads running under the process accessed by HANDLE. Returns the
number of threads. The pr_who fields of these status blocks
contain kernel thread numbers.
*/
int
irix_get_kernel_threads (spyProc_t* handle, prstatus_t** thread_list)
{
if (handle != NULL) {
spyThread_t buffer;
THREAD_INFO (handle)->nthreads = 0;
thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0,
collect_uthread_callback, &buffer, NULL);
*thread_list = THREAD_INFO(handle)->thread_status;
return THREAD_INFO(handle)->nthreads;
}
return 0;
}
static int
update_status_from_thread_callback (spyProc_t* handle, spyThread_t uthread,
void* pthreadp0, void* prstatp0)
{
prstatus_t ustatus;
prstatus_t* prstatp = (prstatus_t*) prstatp0;
spyThread_t* pthreadp = (spyThread_t*) pthreadp0;
if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread, NULL,
&ustatus, NULL) >= 0)
{
ustatus.pr_nthreads = prstatp->pr_nthreads += 1;
ustatus.pr_who = *pthreadp;
if (prstatp->pr_nthreads == 1
|| (prstatp->pr_flags & ~ustatus.pr_flags & (PR_ISTOP | PR_STOPPED))
|| ((prstatp->pr_flags & ustatus.pr_flags & PR_ISTOP)
&& prstatp->pr_why == PR_REQUESTED
&& ustatus.pr_why != PR_REQUESTED))
memcpy (prstatp, &ustatus, sizeof (ustatus));
}
return 0;
}
/*
LOCAL FUNCTION
complete_status -- fill in main process status from current thread.
SYNOPSIS
static void complete_status (spyProc_t* handle, prstatus_t* prstatusp)
DESCRIPTION
Update main process status *PRSTATUSP as needed from kernel threads
controlled by HANDLE (does nothing if HANDLE is null). Attempts to
emulate Solaris threads, in that the flags and registers of the
updated *PRSTATUSP come from a kernel thread that is
not stopped, stopped at an event of interest other than
PR_REQUESTED, or stopped at a REQUESTED stop (in that order of
first to last preference).
*/
static void
complete_status (spyProc_t* handle, prstatus_t* prstatusp)
{
if (handle == NULL)
{
prstatusp->pr_nthreads = 0;
prstatusp->pr_who = 0;
}
else
{
spyThread_t thread;
prstatus_t ustatus;
ustatus.pr_nthreads = 0;
ustatus.pr_who = 0;
thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0,
update_status_from_thread_callback, &thread, &ustatus);
if (ustatus.pr_nthreads > 0)
{
/* Copy in relevant fields from representative uthread. */
prstatusp->pr_flags &=
~(PR_STEP | PR_ISTOP | PR_STOPPED | PR_DSTOP |
PR_ASLEEP | PR_PCINVAL | PR_RETURN);
prstatusp->pr_flags |= ustatus.pr_flags &
(PR_STEP | PR_ISTOP | PR_STOPPED | PR_DSTOP |
PR_ASLEEP | PR_PCINVAL | PR_RETURN);
prstatusp->pr_why = ustatus.pr_why;
prstatusp->pr_what = ustatus.pr_what;
prstatusp->pr_who = ustatus.pr_who;
memcpy (&prstatusp->pr_reg, &ustatus.pr_reg,
sizeof (ustatus.pr_reg));
}
}
}
/*
GLOBAL FUNCTION
irix_get_status -- Get status of process or thread.
SYNOPSIS
int irix_get_status (spyProc_t* handle, int fd, spyThread_t pthread,
prstatus_t* prstatusp)
DESCRIPTION
Fill in *PRSTATUS for the process with /proc file descriptor FD,
thread number PTHREAD. If PTHREAD is 0, gets status of the main process,
and fills in flags, why, what, and register information from
a "representative" kernel thread, if there are any.
If PTHREAD has no kernel context, then gets general registers only,
and "fakes" the status flags to show a PR_REQUESTED stop (no other
information is available for a user thread with no kernel
context). Simply fetches the normal status when called with
a thread that has a kernel context. Returns as for the PIOCSTATUS ioctl.
*/
int
irix_get_status (spyProc_t* handle, int fd, spyThread_t pthread,
prstatus_t* prstatusp)
{
int win;
if (handle == NULL || pthread == 0)
{
/* Main process */
win = ioctl (fd, PIOCSTATUS, prstatusp);
if (win >= 0)
complete_status (handle, prstatusp);
}
else
{
int uthread = get_kernel_thread (handle, pthread);
if (IS_LWP_ID (uthread))
win = thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread, NULL,
prstatusp, NULL);
else
{
/* Thread with no kernel context; fake flags and get the registers */
prstatusp->pr_flags = PR_STOPPED | PR_ISTOP | PR_RETURN;
prstatusp->pr_why = PR_REQUESTED;
prstatusp->pr_what = 0;
win = thread_scan (handle, PIOCGREG, STC_THREAD, pthread, NULL,
&prstatusp->pr_reg, NULL);
}
}
return win;
}
/*
GLOBAL FUNCTION
irix_wait_proc -- Wait for process to stop on event of interest.
SYNOPSIS
int irix_wait_proc (spyProc_t* handle, int fd, prstatus_t* prstatusp)
DESCRIPTION
Waits for the process with open /proc file descriptor FD and
accessed through HANDLE (if non-null) to stop on an "event of interest",
setting *PRSTATUSP as for the PIOCWSTOP ioctl.
*/
int
irix_wait_proc (spyProc_t* handle, int fd, prstatus_t* prstatusp)
{
int win;
if (! libspy_enabled || handle == NULL)
win = ioctl (fd, PIOCWSTOP, prstatusp);
else
{
/* Apparently, PIOCWSTOP hangs when there are kernel threads,
but poll still works. */
struct pollfd request;
request.fd = fd;
request.events = POLLPRI;
win = poll (&request, 1, -1);
if (win < 0)
return win;
win = irix_get_status (handle, fd, 0, prstatusp);
if (win && ! (prstatusp->pr_flags & (PR_STOPPED | PR_ISTOP)))
warning ("irix_wait_proc: process not stopped");
}
return win;
}
static int
resume_step_callback (spyProc_t* handle, spyThread_t uthread_id,
void* pthread_idp0, void* argp0)
{
prstatus_t status;
spyThread_t* pthread_idp = (spyThread_t*) pthread_idp0;
resume_info_t* argp = (resume_info_t*) argp0;
if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread_id, NULL,
&status, NULL) >= 0
&& (status.pr_flags & PR_RETURN))
{
argp->num_resumed += 1;
if (thread_scan (handle, PIOCRUN, STC_THREAD, uthread_id, NULL,
argp->prrun_p, NULL) < 0)
return -1;
}
return 0;
}
/*
GLOBAL FUNCTION
irix_resume -- Resume process or thread.
SYNOPSIS
int irix_resume (spyProc_t* handle, int fd, spyThread_t pthread,
prrun_t* prrun_p)
DESCRIPTION
Resume thread PTHREAD (all threads if 0) of the process with open
file descriptor FD and accessed through HANDLE (if non-null). PRRUN_P
is as for the PIOCRUN ioctl.
*/
int
irix_resume (spyProc_t* handle, int fd, spyThread_t pthread, prrun_t* prrun_p)
{
if (! (prrun_p->pr_flags & PRSTEP))
prrun_p->pr_flags |= PRCSTEP;
if (pthread == 0 && handle != NULL && (prrun_p->pr_flags & PRSTEP))
{
/* Apparently, we will hang in irix_wait_proc if we attempt to
step any thread that does not have PR_RETURN on, so avoid
doing so. */
resume_info_t args;
spyThread_t thread;
int win;
args.prrun_p = prrun_p;
args.num_resumed = 0;
win = thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0,
resume_step_callback, &thread, &args);
if (args.num_resumed > 0 && win >= 0)
return 0;
}
if (pthread == 0)
return ioctl (fd, PIOCRUN, prrun_p);
else
return thread_scan (handle, PIOCRUN, STC_THREAD,
get_kernel_thread (handle, pthread), NULL,
prrun_p, NULL);
}
/*
GLOBAL FUNCTION
irix_reset_held_signals -- Restore kernel thread held signals
SYNOPSIS
int irix_reset_held_signals (spyProc_t* handle)
DESCRIPTION
Restore the held signal masks for all kernel threads present at the
time HANDLE was created. (On IRIX, held signals are per-kernel
thread, if there are any kernel threads.)
*/
int
irix_reset_held_signals (spyProc_t* handle)
{
int code;
if (handle == NULL)
code = 0;
else
{
thread_data_t* info = THREAD_INFO (handle);
int i;
code = 1;
for (i = 0; i < info->num_initial_uthreads; i += 1)
if (thread_scan (handle, PIOCSHOLD, STC_THREAD,
info->initial_uthread_status[i].pr_who, NULL,
&info->initial_uthread_status[i].pr_sighold, NULL) < 0)
code = 0;
}
return code;
}
/*
GLOBAL FUNCTION
irix_get_fpregs -- Get floating-point registers
SYNOPSIS
int irix_get_fpregs (spyProc_t* handle, spyThread_t tid,
gdb_fpregset_t* fpregset)
DESCRIPTION
Fetch floating-point registers (to FPREGSET) for thread TID of process
accessed through HANDLE.
*/
int
irix_get_fpregs (spyProc_t* handle, spyThread_t tid,
gdb_fpregset_t* fpregset)
{
return thread_scan (handle, PIOCGFPREG, STC_THREAD, tid,
NULL, fpregset, NULL);
}
/*
GLOBAL FUNCTION
irix_set_gregs -- Set general registers
SYNOPSIS
int irix_set_gregs (spyProc_t* handle, spyThread_t tid, gdb_gregset_t* gregs)
DESCRIPTION
Set general registers (from GREGS) for thread TID of process
accessed through HANDLE.
*/
int
irix_set_gregs (spyProc_t* handle, spyThread_t tid, gdb_gregset_t* gregs)
{
return thread_scan (handle, PIOCSREG, STC_THREAD, tid,
NULL, gregs, NULL);
}
/*
GLOBAL FUNCTION
irix_set_fregs -- Set general registers
SYNOPSIS
int irix_set_fregs (spyProc_t* handle, spyThread_t tid,
gdb_fpregset_t* fregs)
DESCRIPTION
Set floating-point registers (from FREGS) for thread TID of process
accessed through HANDLE.
*/
int
irix_set_fregs (spyProc_t* handle, spyThread_t tid, gdb_fpregset_t* fregs)
{
return thread_scan (handle, PIOCSFPREG, STC_THREAD, tid,
NULL, fregs, NULL);
}
/* Debugging utilities */
static int
dbg_callback1 (spyProc_t* handle, spyThread_t id1, void* id2p0, void* unused)
{
spyThread_t* id2p = (spyThread_t*) id2p0;
fprintf (stderr, "uthread: %x, pthread: %x\n", id1, *id2p);
return 0;
}
static void
procfs_dbg_cgname (spyProc_t* handle, spyThread_t id)
{
spyThread_t result;
thread_scan (handle, SPYCGNAME, STC_THREAD, id, NULL, &result, NULL);
fprintf (stderr, "%x -> %x\n", id, result);
}
static void
procfs_dbg_uthreads (spyProc_t* handle)
{
spyThread_t result;
thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0,
dbg_callback1, &result, NULL);
}
#endif /* HAVE_SPYTHREAD */
/* Register that we are able to handle irix5 core file formats.
This really is bfd_target_unknown_flavour */
static struct core_fns irix5_core_fns =
{
bfd_target_unknown_flavour, /* core_flavour */
default_check_format, /* check_format */
default_core_sniffer, /* core_sniffer */
fetch_core_registers, /* core_read_registers */
NULL /* next */
};
void
_initialize_core_irix5 (void)
{
add_core_fns (&irix5_core_fns);
}
|