Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1789 lines
48 KiB
C

static char sccsid[] = "@(#)74 1.49.2.39 src/bos/usr/ccs/lib/libdbx/process.c, libdbx, bos411, 9438B411a 9/21/94 14:31:51";
/*
* COMPONENT_NAME: (CMDDBX) - dbx symbolic debugger
*
* FUNCTIONS: CheckDataAddr, CheckTextAddr, cacheflush, fetch, fpregval,
* getinfo, getldrinfo, getpc, getprog, infrom, noexec_file_map,
* outto, pcont, pio, printloaderinfo, printptraceinfo,
* process_init, psigtrace, pstart, pterm, reg, setinfo, setreg,
* setregs, setsigtrace, sigs_off, sigs_on, store, unsetsigtraces,
* usignal, sync_load_info, debuggee_setpgrp, debugger_setpgrp
* gettid, getsigstatus, getheapsize
*
* ORIGINS: 26, 27, 83
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1994
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
* Copyright (c) 1982 Regents of the University of California
*
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#ifdef KDBXRT
#include "rtnls.h" /* MUST BE FIRST */
#endif
/* include file for message texts */
#include "dbx_msg.h"
nl_catd scmc_catd; /* Cat descriptor for scmc conversion */
/*
* Process management.
*
* This module contains the routines to manage the execution and
* tracing of the debuggee process.
*/
#include "defs.h"
#include "envdefs.h"
#include <sys/types.h>
#include "process.h"
#include "execute.h"
#include "ops.h"
#include "eval.h"
#include "object.h"
#include "main.h"
#include "coredump.h"
#include "cma_thread.h"
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/stat.h>
#include <sys/FP.h>
#include <sys/core.h>
#ifndef KDBX
#include <sys/systemcfg.h>
#endif /* KDBX */
#ifdef K_THREADS
#include <sys/uthread.h>
#endif /* K_THREADS */
#if defined (CMA_THREAD) || defined(K_THREADS)
#include "k_thread.h"
#endif /* CMA_THREAD || K_THREADS*/
#include "cma_thread.h"
#ifdef _THREADS
#include <procinfo.h>
#endif /* _THREADS */
#define LDCHUNK 4096
#define TRAPI_OPCODE 3
#define TRAP_OPCODE 31
#ifdef KDBX
#define PT_READ_U 3
#define U_READ PT_READ_U
#endif /* KDBX */
/* NOTE : the opcode of an assembly instruction in in the
first 6 bits (bits 0 through 5). The length of
an assembly instruction is 32 bits. To isolate
the opcode, shift the instruction right 26 bits
(32 - 6) */
/* macro to isolate the opcode from an instruction */
#define get_opcode(inst) (inst >> 26)
/* macro to determine if an instruction is a trap */
#define is_trap_instruction(x) ((get_opcode(x) == TRAPI_OPCODE) \
|| (get_opcode(x) == TRAP_OPCODE))
public integer loadcnt = 0;
extern Boolean traceback_table; /* This is defined in frame.c */
extern Boolean just_started; /* These are defined in execute.c */
extern Boolean noexec,textaddrs;
extern String errfile;
extern Address text_reloc, data_reloc;
extern String corename;
extern Ttyinfo ttyinfo;
extern Ttyinfo ttyinfo_in;
private struct Process pbuf;
struct ldinfo *loader_info;
struct fdinfo *fd_info;
unsigned short current_hardware;
extern struct core_dump corehdr;
extern unsigned coresize;
pid_t debugger_pgrp; /* process group of dbx */
pid_t debuggee_pgrp; /* progess group of debuggee */
static boolean pgrps_set = false;
static boolean job_control = true;
#ifdef _THREADS
/* typedef and pointers used by gettid() and getstatus() : access to processe */
/* information and threads information */
typedef struct thrdsinfo thrdsinfo;
typedef struct mstsave mstsave;
typedef struct procsinfo procsinfo;
public int *loader_offset; /* offset in core file of data region */
public int nb_k_threads = 0; /* number of kernel threads */
public tid_t tid_running_thread = 0; /* tid of the running_thread */
public thrdsinfo *pthrdsinfo = NULL; /* pointer to a buffer where thrdsinfo */
/* structures are stored by getthrds() */
public struct procsinfo *pprocsinfo = NULL; /* pointer to a buffer where */
/* procsinfo structure is stored */
/* by getprocs() */
#endif /* _THREADS */
private Word fetch();
private store();
private infrom();
private outto();
#ifndef _NO_PROTO
void (*save_sig)(int);
#else
void (*save_sig)();
#endif
static int debuggee_fd = 0; /* When screen(1) is called, debuggee_fd
will be set to the originating tty,
otherwise, it is 0. */
static boolean called_debuggee_setpgrp = false; /* Used to ensure that */
/* debugge[er]_setpgrp() */
/* only get called in pairs */
/*
* Initialize process information.
*/
public process_init ()
{
register int i;
char buf[10];
struct stat core_times, obj_times, prog_times;
struct user uarea;
String base_objname, base_progname;
process = &pbuf;
if (!pgrps_set) { /* only set these once */
debuggee_pgrp = debugger_pgrp = getpgrp();
pgrps_set = true;
/*
* This test tries to determine if the parent shell supports
* job control or not. The test passes if the shell is sh (which
* does not have job control) and fails for ksh and csh (which do have
* job control). However, if the behavior of the shells change, or if
* other factors affect the output of these sys calls, or if some
* other shells are used (tcsh, bash, etc...), this test may not
* be valid.
* The whole reason for this test is so that we do not call
* tcsetpgrp() if there is no job control.
*/
#ifdef KDBXRT
job_control = false;
#else
if (getpid() != getpgrp() && getppid() == getpgrp())
job_control = false;
#endif
}
process->status = (coredump || norun || noexec) ? STOPPED : NOTSTARTED;
setsigtrace();
process->is_bp = false;
/*
* Determine what hardware we are currently running on. Set external
* variable current_hardware to tell this
*/
#if defined(_POWER) && !defined(KDBX)
if( __power_rs1() || __power_rsc() ) {
current_hardware = PWR;
} else if( __power_rs2() ) {
current_hardware = PWRX;
} else if( __power_601() ) {
current_hardware = SET_601;
} else if( __power_603() ) {
current_hardware = SET_603;
} else if( __power_604() ) {
current_hardware = SET_604;
} else {
/*
* Not one of our recognized hardware types. Set to ANY
*/
current_hardware = ANY;
}
#else
current_hardware = ANY;
#endif
getfptype(&fpregs);
for (i = 0; i < NGREG; i++) {
sprintf(buf, "$r%d", i);
defregname(identname(buf, false), i);
sprintf(buf, "$R%d", i);
defregname(identname(buf, false), i);
}
for (i = 0; i < fpregs; i++) {
sprintf(buf, "$fr%d", i);
deffregname(identname(buf, false), i + NGREG + NSYS);
sprintf(buf, "$FR%d", i);
deffregname(identname(buf, false), i + NGREG + NSYS);
}
defsysregs();
/*
* Check to make sure we are reading a valid core file.
* We do this by (1) making sure the core file was generated
* by the current program we are debugging and (2) by making
* sure the core file is newer.
*/
if (coredump)
{
fread((void *) &corehdr, (size_t) 1, (size_t) CHDRSIZE, corefile);
rewind(corefile);
stat(corename, &core_times);
coresize = (unsigned) core_times.st_size;
getldrinfo(true);
if (coredump) {
/* Strip path to object file if it exists. */
base_objname = (String) strrchr(objname,'/');
if (!base_objname)
base_objname = objname;
else
base_objname++;
/* Strip path to exec name from core file if it exists. */
base_progname = (String) strrchr(fd_info[0].pathname,'/');
if (!base_progname)
base_progname = fd_info[0].pathname;
else
base_progname++;
if (streq(corename,"/dev/mem") || streq(corename,"/dev/kmem")) {
coredump_readin(process->reg, process->freg, process->signo);
} else {
stat(objname, &obj_times);
stat(base_progname, &prog_times);
/* verify No. 1 */
if ((!strcmp(base_progname,base_objname)) ||
/* determine if linked file */
((prog_times.st_dev == obj_times.st_dev) &&
(prog_times.st_ino == obj_times.st_ino))) {
/* No. 2 */
if (obj_times.st_mtime <= core_times.st_mtime)
coredump_readin(process->reg, process->freg,
process->signo);
else {
coredump = false;
error( catgets(scmc_catd, MS_process, MSG_267,
"Core file is older than current program (core ignored)"));
}
#ifdef _THREADS
if (!corehdr.c_flag & CORE_VERSION_1) {/* AIX 4 ?*/
coredump = false;
error( catgets(scmc_catd, MS_process, MSG_538,
"%s is not a valid core file (core ignored)"),
corename);
}
#endif /* _THREADS */
} else {
coredump = false;
error( catgets(scmc_catd, MS_process, MSG_537,
"Core file program (%s) does not match current program \
(core ignored)"), base_progname );
}
}
}
}
if (coredump || noexec) {
for (i = 0; i <= regword(NGREG+NSYS+MAXFREG+1-1); i++) {
process->valid[i] = 0xffffffff;
process->dirty[i] = 0;
}
pc = reg(SYSREGNO(PROGCTR));
} else {
process->status = (norun) ? STOPPED : NOTSTARTED;
}
arginit();
}
/*
* getldrinfo: get necessary loader information from the process
* Note: the loader information is actually slightly different
* from the ldinfo structure used here. To see the actual
* loader structure, see <sys/ldr.h>.
*/
public getldrinfo (iscoreimage)
Boolean iscoreimage;
{
char *ldinfo_ptr;
boolean badinfo = true;
unsigned int ldinfosz = LDCHUNK;
unsigned int ldcnt = 0;
unsigned int i;
if (iscoreimage) {
if (vaddrs) { /* Create the core file */
loader_info = (struct ldinfo *) malloc(sizeof(struct ldinfo));
fd_info = (struct fdinfo *) malloc(sizeof(struct fdinfo));
loader_info->ldinfo_next = 0;
loader_info->ldinfo_fd = 0;
loader_info->textorg = 0;
loader_info->textsize = 0xffffffff;
loader_info->dataorg = 0;
loader_info->datasize = 0xffffffff;
fd_info->pathname = objname;
fd_info->membername = (char *) 0;
ldcnt = 1; /* Allow perusal of the object file only. */
loadcnt = ldcnt;
return;
} else {
/* Stack is after ldinfop in core file. */
if (((unsigned) corehdr.c_stack) > ((unsigned) corehdr.c_tab)) {
#ifdef _THREADS
if ( corehdr.c_msts)
ldinfosz = (unsigned int) ((unsigned) corehdr.c_msts) -
((unsigned) corehdr.c_tab);
else
ldinfosz = (unsigned int) ((unsigned) corehdr.c_stack) -
((unsigned) corehdr.c_tab);
#else
ldinfosz = (unsigned int) ((unsigned) corehdr.c_stack) -
((unsigned) corehdr.c_tab);
#endif /* _THREADS */
ldinfo_ptr = (char *) malloc(ldinfosz);
#ifdef _THREADS
if (((Address)corehdr.c_tab) >= CHDRSIZE &&
(((Address)corehdr.c_tab) < coresize))
#else
if ((((Address)corehdr.c_tab) >= (sizeof(corehdr.c_u) +
(Address)(&corehdr.c_u) - (Address)(&corehdr.c_signo))) &&
(((Address)corehdr.c_tab) < coresize))
#endif /* _THREADS */
{
fseek(corefile, (long int) corehdr.c_tab, 0);
fread((void *) ldinfo_ptr, (size_t) 1,
(size_t) ldinfosz, corefile);
} else {
coredump = false;
error( catgets(scmc_catd, MS_process, MSG_538,
"%s is not a valid core file (core ignored)"), corename);
if (ldinfo_ptr) {
free((void *)ldinfo_ptr);
}
return;
}
} else {
coredump = false;
if (corehdr.c_stack)
error( catgets(scmc_catd, MS_process, MSG_538,
"%s is not a valid core file (core ignored)"),
corename);
else
error( catgets(scmc_catd, MS_process, MSG_550,
"%s does not contain a stack (core ignored)"),
corename);
return;
}
}
} else if (!noexec) {
ldinfo_ptr = (char *) malloc(LDCHUNK);
badinfo = (boolean) ptrace(PT_LDINFO,process->pid,ldinfo_ptr,
ldinfosz,0);
for (; badinfo; ) {
ldinfosz += LDCHUNK;
ldinfo_ptr = (char *) realloc(ldinfo_ptr,ldinfosz);
badinfo = (boolean) ptrace(PT_LDINFO,process->pid,ldinfo_ptr,
ldinfosz,0);
}
}
loader_info = (struct ldinfo *) malloc(ldinfosz);
#ifdef _THREADS
if (coredump)
loader_offset = (int *) malloc(corehdr.c_entries * sizeof(int));
#endif /* _THREADS */
fd_info = (struct fdinfo *) malloc(ldinfosz/3);
if (!noexec) {
/* Rearrange the information so that it is manageable. */
do {
memcpy(&loader_info[ldcnt],ldinfo_ptr,sizeof(struct ldinfo));
#ifdef _THREADS
/*the field ldinfo_fd contains the offset in the core file of the */
/* data region associated with the loaded module */
if (coredump) loader_offset[ldcnt] = loader_info[ldcnt].ldinfo_fd;
#endif /* _THREADS */
fd_info[ldcnt].pathname = ldinfo_ptr + sizeof(struct ldinfo);
fd_info[ldcnt].membername =
fd_info[ldcnt].pathname +
strlen(fd_info[ldcnt].pathname) + 1;
ldinfo_ptr += loader_info[ldcnt].ldinfo_next;
} while (loader_info[ldcnt++].ldinfo_next != 0);
} else {
/* This will be fixed up after reading in the object header */
loader_info->ldinfo_next = 0;
loader_info->ldinfo_fd = 0;
loader_info->textorg = 0;
loader_info->textsize = 0;
loader_info->dataorg = 0;
loader_info->datasize = 0;
fd_info->pathname = objname;
fd_info->membername = (char *) 0;
ldcnt = 1; /* Allow perusal of the object file only. */
}
loadcnt = ldcnt;
}
sync_load_info(prev_ldinfo,prev_fdinfo,prev_ldcnt)
int prev_ldcnt;
struct ldinfo *prev_ldinfo;
struct fdinfo *prev_fdinfo;
{
int oldndx = 1; /* No need to check execed prog, is always = 0 */
int ldndx; /* Indices for previous load info map */
struct ldinfo *old_ld = prev_ldinfo;
struct fdinfo *old_fd = prev_fdinfo;
struct ldinfo *new_ld = loader_info;
struct fdinfo *new_fd = fd_info;
int *found_match; /* Not newly loaded objects. */
int *retained_prog; /* Previously loaded objects. */
found_match = (int *) calloc(loadcnt,sizeof(int));
retained_prog = (int *) calloc(prev_ldcnt,sizeof(int));
for (;oldndx <= prev_ldcnt-1; oldndx++) {
for (ldndx = 1;ldndx <= loadcnt-1; ldndx++) {
/* If address maps and object names the same, they're the same */
if (((old_ld[oldndx].textorg == new_ld[ldndx].textorg)) &&
((old_ld[oldndx].textsize == new_ld[ldndx].textsize)) &&
((old_ld[oldndx].dataorg == new_ld[ldndx].dataorg)) &&
((old_ld[oldndx].datasize == new_ld[ldndx].datasize)) &&
/* check file name */
(streq(old_fd[oldndx].pathname,new_fd[ldndx].pathname)) &&
/* if archive, must check member name */
((!(old_fd[oldndx].membername ||
new_fd[ldndx].membername)) ||
(((old_fd[oldndx].membername &&
new_fd[ldndx].membername)) &&
(streq(old_fd[oldndx].membername,
new_fd[ldndx].membername))))){
/* swap in old information if same */
retained_prog[oldndx] = ldndx;
found_match[ldndx] = oldndx;
break;
}
}
}
update_loaded_object(prev_ldcnt, loadcnt, retained_prog,
found_match, old_ld);
}
/*
* noexec_file_map - Set up maps for non-executable debuggees
*/
void noexec_file_map(magic, txtstart, txtlen, datastart, datalen, fd)
short magic;
Address txtstart, txtlen, datastart, datalen;
int fd;
{
loader_info->ldinfo_fd = 0;
if (exec_object(magic)) {
loader_info->textorg = txtstart;
loader_info->textsize = txtlen;
loader_info->dataorg = datastart;
loader_info->datasize = datalen;
} else {
loader_info->textorg = 0;
loader_info->textsize = 0x7fffffff;
loader_info->dataorg = 0;
loader_info->datasize = 0x7fffffff;
}
loadcnt = 1;
}
/*
* printloaderinfo - dump the loader table information
*/
printloaderinfo()
{
int ldcnt;
Boolean lastentry = false;
for (ldcnt = 0; !lastentry; ldcnt++) {
(*rpt_output)(stdout, "Entry %d:\n",ldcnt+1);
(*rpt_output)(stdout, " Object name: %s\n", fd_info[ldcnt].pathname);
if ((fd_info[ldcnt].membername != nil) &&
(fd_info[ldcnt].membername[0] != '\0'))
(*rpt_output)(stdout, " Member name: %s\n",
fd_info[ldcnt].membername);
(*rpt_output)(stdout, " Text origin: 0x%x\n",
loader_info[ldcnt].textorg);
(*rpt_output)(stdout, " Text length: 0x%x\n",
loader_info[ldcnt].textsize);
(*rpt_output)(stdout, " Data origin: 0x%x\n",
loader_info[ldcnt].dataorg);
(*rpt_output)(stdout, " Data length: 0x%x\n",
loader_info[ldcnt].datasize);
(*rpt_output)(stdout, " File descriptor: 0x%x\n",
loader_info[ldcnt].ldinfo_fd);
if (loader_info[ldcnt].ldinfo_next == 0)
lastentry = true;
(*rpt_output)(stdout, "\n");
}
}
public getprog (pid,exec) /* determine prog name from pid */
int pid; /* attach or exec */
boolean exec;
{
int errcond;
int rc;
FILE *f;
char syscall[512];
char *prognm;
int status = 0;
attach = (boolean)(!exec); /* set attach to program flag */
norun = true; /* run and re-run not allowed */
coredump = false; /* Ignore core dumps in this case */
if (attach) {
if (reattach) /* if doing reattach instead */
{
status = 0x7f; /* process stopped */
errcond = ptrace(REATT, pid, 0, 0, 0); /* reattach to program */
} else
errcond = ptrace(ATTACH, pid, 0, 0, 0); /* attach to program */
if (errcond < 0) /* if error on attach */
fatal( catgets(scmc_catd, MS_process, MSG_277,
"could not attach to pid %d\n"), pid);
else {
(*rpt_output)(stdout, catgets(scmc_catd, MS_process, MSG_278,
"Waiting to attach to process %d ...\n"), pid);
ptraced(pid);
while (status == 0)
pwait(pid, &status);
}
}
else {
(*rpt_output)(stdout, catgets(scmc_catd, MS_process, MSG_279,
"Attaching to process from exec...\n"));
}
process = &pbuf;
process->pid = pid;
getldrinfo(false);
#if defined (CMA_THREAD) || defined(K_THREADS)
threads(th_get_info, nil); /* get info about any existing threads */
if(lib_type == KERNEL_THREAD) {
/* update also $ci $mi and $ai */
condition_k(th_get_info,nil);
attribute_k(th_get_info,nil);
mutex_k(th_get_info,nil);
}
#endif /* CMA_THREAD || K_THREADS*/
openfiles(false);
isstopped = true;
}
/*
* Routines to get at process information from outside this module.
*/
public Word reg (n)
int n;
{
register Process p;
struct user u;
p = process;
if ((p->valid[regword(n)] & regbit(n)) == 0) {
if (n < NGREG) { /* General purpose register */
p->reg[n] = readreg(p, n);
} else if (n < (NGREG+NSYS)) { /* System register */
p->reg[n] = readreg(p, REGTOSYS(n));
#if defined(_IBMRT) && !defined(KDBXRT)
if (REGTOSYS(n) == CR) {
p->reg[n] = (p->reg[n] >> 16) & 0xffff;
} else if (REGTOSYS(n) == ICS) {
p->reg[n] &= 0xffff;
}
#endif
} else { /* Floating point register */
readflreg(p, FREGTOSYS(n - NGREG - NSYS),p->freg[n - NGREG - NSYS]);
return (Word) p->freg[n - NGREG - NSYS];
}
p->valid[regword(n)] |= regbit(n);
}
return p->reg[n];
}
/*
* Routines to get at floating point registers.
*/
public double fpregval (n)
int n;
{
register Process p;
int regno;
p = process;
regno = NGREG+NSYS+n; /* Overall register number. */
if ((p->valid[regword(regno)] & regbit(regno)) == 0) {
readflreg(p, FREGTOSYS(n), p->freg[n]);
p->valid[regword(regno)] |= regbit(regno);
}
return p->freg[n];
}
public setreg (n, w)
int n;
Word w;
{
register Process p;
static union { Word fregp[2];
double fvalue;
} fpvalue;
int x = n;
p = process;
if (n < 0) {
n *= -1;
fpvalue.fregp[1] = w;
p->freg[n-NGREG-NSYS] = fpvalue.fvalue;
}
else if (n < NGREG) {
p->reg[n] = w;
} else if (n < NGREG + NSYS) {
#if defined(_IBMRT) && !defined(KDBXRT)
if (n == CR)
p->reg[n] = w & 0xffff;
else
#endif
p->reg[n] = w;
}
else {
fpvalue.fregp[0] = w;
}
#if defined (CMA_THREAD) || defined (K_THREADS)
if (running_thread != nil && current_thread != nil &&
running_thread != current_thread && (lib_type != KERNEL_THREAD)) {
/* If we have CMA thread and the current thread is not the running */
/* thread, we need to write into the stack and not set the dirty bit */
/* No only do we need to modify reg. values of Process, we need to */
/* write the values into the sigcontext structure stored on stack. */
setThreadreg(x, w); /* x is original value of n */
process->valid[regword(n)] |= regbit(n);
} else {
/* if we have kernel threads (and libpthreads 1:1) the registers are */
/* updated by setregs when the application is resume or when the */
/* user execute the subcommand : thread current n */
#endif /* CMA_THREAD || K_THREADS */
process->dirty[regword(n)] |= regbit(n);
process->valid[regword(n)] |= regbit(n);
#if defined (CMA_THREAD) || defined (K_THREADS)
}
#endif /* CMA_THREAD || K_THREADS */
}
/*
* Start up a new process by forking and exec-ing the
* given argument list, returning when the process is loaded
* and ready to execute. The PROCESS information (pointed to
* by the first argument) is appropriately filled.
*
* If the given PROCESS structure is associated with an already running
* process, we terminate it.
*/
pstart (p, argv, infile, outfile)
Process p;
String argv[];
String infile;
String outfile;
{
int status;
if (norun) {
getinfo(p, STOPPED);
ptraced(p->pid);
return;
}
if (p->pid != 0) {
pterm(p);
cacheflush(p);
}
fflush(stdout);
#ifdef KDBX
p->pid = kdbx_fork();
#else
p->pid = fork();
#endif
if (p->pid == -1) {
panic( catgets(scmc_catd, MS_process, MSG_280, "cannot fork"));
}
if (ischild(p->pid)) {
nocatcherrs();
traceme();
/* Reset stdio if Screen Command */
if (ScrUsed)
{
close(0);
dup(debuggee_fd);
close(1);
dup(debuggee_fd);
close(2);
dup(debuggee_fd);
}
/* Check for redirection AFTER reset of stdio */
if (infile != nil) {
infrom(infile);
}
if ((outfile != nil) || (errfile != nil)) {
outto(outfile, errfile);
}
execv(argv[0], argv);
_exit(1);
}
pwait(p->pid, &status);
getinfo(p, status);
if (p->status != STOPPED) {
beginerrmsg();
(*rpt_error)(stderr, catgets(scmc_catd, MS_process, MSG_281,
"warning: cannot execute %s\n"), argv[0]);
noexec = true;
text_reloc = data_reloc = 0;
defvar(identname("$text",true),nil);
textaddrs = true;
objfile_readin();
just_started = true;
} else {
just_started = true;
ptraced(p->pid);
noexec = false;
}
}
/*
* Terminate a ptrace'd process.
*/
public pterm (p)
Process p;
{
int status;
if (p != nil and p->pid != 0) {
ptrace(PKILL, p->pid, 0, 0, 0);
if (!attach) /* We are not the parent, so we won't get the signal */
pwait(p->pid, &status);
unptraced(p->pid);
}
}
/*
* Continue a stopped process. The first argument points to a Process
* structure. Before the process is restarted it's user area is modified
* according to the values in the structure. When this routine finishes,
* the structure has the new values from the process's user area.
*
* Pcont terminates when the process stops with a signal pending that
* is being traced (via psigtrace), or when the process terminates.
*/
pcont (p, signo)
Process p;
int signo;
{
int s, status;
boolean skip_load;
extern boolean loadpending;
extern boolean lazy;
extern int last_module;
extern refreshbp();
if (p->pid == 0) {
error( catgets(scmc_catd, MS_process, MSG_282,
"program is not active"));
return -1;
}
s = signo;
do {
skip_load = false;
setinfo(p, s);
if (traceexec) {
(*rpt_output)(stdout, "!! pcont from 0x%x with signal %d (%d)\n",
reg(SYSREGNO(PROGCTR)), s, p->signo);
}
sigs_off();
/* We don't need to tell the OS what it already knows...
if (ptrace(CONT, p->pid, p->reg[SYSREGNO(PROGCTR)], p->signo) < 0) {
*/
#if defined ( K_THREADS) && !defined (KDBX)
/* resume a list of threads : PTT_CONTINUE */
thread_k_cont(p); /* to see if the running_thread is held */
#else
if (traceexec) {
(*rpt_output)(stdout, "!! ptrace(%d,0x%x,%d,%d)\n",
CONT, p->pid, 1, p->signo, 0);
}
if (ptrace(CONT, p->pid, 1, p->signo, 0) < 0) {
panic( catgets(scmc_catd, MS_process, MSG_285,
"error %d trying to continue process"), errno);
}
#endif /* K_THREADS && !KDBX */
(*rpt_executing)( );
pwait(p->pid, &status);
if (traceexec) {
(*rpt_output)(stdout, "!! wait status = 0x%x, errno = %d\n",
status, errno);
}
action_mask |= EXECUTION;
action_mask &= ~CONTEXT_CHANGE;
action_mask &= ~LISTING;
action_mask &= ~ELISTING;
sigs_on();
getinfo(p, status);
/* if the user has set "debug 3" (traceexec is true) and
we are not going to stop for this signal */
if (traceexec && (p->status == STOPPED)
&& !istraced(p) && !(p->is_bp)) {
/* print debug message */
(*rpt_output)(stdout, "!! ignored signal %d at 0x%x\n",
p->signo, reg(SYSREGNO(PROGCTR)));
fflush(stdout);
}
s = p->signo;
/* In case user don't care about load() and unload()... */
if (loadpending and varIsSet("$ignoreload")) {
s = 0; /* reset the cont signal to zero */
skip_load = true; /* go thru the do loop once more */
loadpending = false; /* reset loadpending */
refreshbp(); /* refresh breakpoints after load()*/
if (lazy) /* need to reread string and debug */
last_module = -2; /* table after load or unload */
}
/* while we want to ignore this signal and continue execution */
} while ((p->status == STOPPED && !istraced(p) && !(p->is_bp))
|| skip_load);
if (traceexec) {
(*rpt_output)(stdout, "!! pcont to 0x%x on signal %d\n",
reg(SYSREGNO(PROGCTR)), p->signo);
fflush(stdout);
}
}
/*
* Return from execution when the given signal is pending.
*/
public psigtrace (p, sig, sw)
Process p;
int sig;
Boolean sw;
{
if (sw) {
if (sig <= (8*sizeof(Word)))
p->sigset[0] |= setrep(sig);
else
p->sigset[1] |= setrep(sig-32);
} else {
if (sig <= (8*sizeof(Word)))
p->sigset[0] &= ~setrep(sig);
else
p->sigset[1] &= ~setrep(sig-32);
}
}
/*
* Set up what signals we want to trace.
*/
setsigtrace ()
{
register int i;
register Process p;
p = process;
for (i = 1; i <= NSIG; i++) {
psigtrace(p, i, true);
}
psigtrace(p, SIGHUP, false);
psigtrace(p, SIGKILL, false);
psigtrace(p, SIGALRM, false);
psigtrace(p, SIGCLD, false);
#if defined (CMA_THREAD) || defined (_THREADS)
psigtrace(p, SIGVIRT, false);
#endif /* CMA_THREAD */
}
/*
* Don't catch any signals.
* Particularly useful when letting a process finish uninhibited.
*/
public unsetsigtraces (p)
Process p;
{
p->sigset[0] = 0;
p->sigset[1] = 0;
}
/*
* This interrupt handler is used if dbx gets interrupt (SIGINT)
* while it is waiting for the child (debuggee) to come back after
* a "cont" ,"step", or "next", etc...
* Normally dbx ignores all signals during this wait, but we need
* to handle SIGINT specially so that there's a way to interrupt
* dbx in cases of continue after attaching to a process. In which
* case, there might not be a tty for the child to accept interrupts
* (control-c).
*
* We do this by interrupting the child debuggee which in turn
* will stop dbx's wait.
*/
private void intrchild()
{
signal(SIGINT, intrchild); /* reset the signal handler */
kill(process->pid, SIGINT); /* stop debuggee using SIGINT */
}
/*
* Turn off attention to signals not being caught.
*/
private Voidfunc sigfunc[NSIG];
public sigs_off ()
{
register int i;
for (i = FIRSTSIG; i < LASTSIG; i++) {
if (i == SIGINT) {
sigfunc[i] = signal(i, intrchild);
} else if (i != SIGKILL) {
sigfunc[i] = signal(i, SIG_IGN);
}
}
}
/*
* Turn back on attention to signals.
*/
public sigs_on ()
{
register int i;
for (i = FIRSTSIG; i < LASTSIG; i++) {
if (i != SIGKILL) {
signal(i, sigfunc[i]);
}
}
}
#ifndef PT_READ_U
/*
* NAME: getheapsize
*
* FUNCTION: Access to procsinfo to get heap size
*
* PARAMETERS:
* p - process id for current process
*
* NOTES: Used by dpi_check_heap_and_stacks
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES: none
*
* RETURNS: end of heap (if successful)
* 0 (if malloc or getprocs fails)
*/
public uint getheapsize (pid_t pid)
{
uint heap_size=0;
if (pprocsinfo == NULL)
{
pprocsinfo=(procsinfo *) malloc ((size_t)(sizeof(procsinfo)));
}
if (pprocsinfo == NULL)
{
panic( catgets(scmc_catd, MS_pthread, MSG_773, "malloc error"));
}
else
{
if (getprocs(pprocsinfo, sizeof(procsinfo), NULL,
sizeof(struct fdsinfo), &pid, 1) < 0)
{
panic( catgets(scmc_catd,MS_pthread, MSG_770,
"could not access to procsinfo errno %d"), errno);
}
else
{
heap_size = (uint)pprocsinfo->pi_dsize;
}
}
return(heap_size);
}
#endif
#ifdef _THREADS
/*
* NAME: getsigstatus
*
* FUNCTION: Access to procsinfo to get sigstatus
*
* PARAMETERS:
* p - structure Process to read
* sig - signal
*
* NOTES: Used by getinfo and usignal
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES:
*
* RETURNS: return address associated with the signal
*/
public Address getsigstatus (p,sig)
register Process p;
int sig;
{
pid_t index;
if (pprocsinfo == NULL)
pprocsinfo=(procsinfo *) malloc ((size_t)(sizeof(procsinfo)));
if (pprocsinfo == NULL)
panic( catgets(scmc_catd, MS_pthread, MSG_773, "malloc error"));
index = p->pid;
if (getprocs(pprocsinfo, sizeof(procsinfo), NULL,
sizeof(struct fdsinfo), &index, 1) < 0) {
panic( catgets(scmc_catd,MS_pthread, MSG_770,
"could not access to procsinfo errno %d"), errno);
}
return(pprocsinfo->pi_signal[sig]);
}
/*
* NAME: gettid
*
* FUNCTION: Access to thrdsinfo : access to the running_thread
*
* PARAMETERS:
* p - structure Process
*
* NOTES: Used by getinfo
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES: update reg in Process structure p.
* and sigcode
*
* RETURNS: thread_id of running_thread
*/
public tid_t gettid (p)
register Process p;
{
register int r,n,i;
int index = 0;
thrdsinfo *ptr;
/* first call to know the number of threads */
r=getthrds(p->pid,NULL,sizeof(thrdsinfo),&index,1000000);
if (r < 0) {
panic( catgets(scmc_catd, MS_pthread, MSG_771,
"could not access to thrdsinfo errno %d"), errno);
}
if ((r > nb_k_threads || pthrdsinfo == NULL) && (r > 0)) {
if (pthrdsinfo) free(pthrdsinfo);
pthrdsinfo=(thrdsinfo *) malloc ((size_t)(r * sizeof(thrdsinfo)));
if ( pthrdsinfo == NULL)
panic( catgets(scmc_catd, MS_pthread, MSG_773, "malloc error"));
}
/* second call to read the threads information */
nb_k_threads = r;
index = 0;
n = getthrds(p->pid,pthrdsinfo,sizeof(thrdsinfo),&index,r);
if (n < 0) {
panic( catgets(scmc_catd, MS_pthread, MSG_771,
"could not access to thrdsinfo errno %d"), errno);
}
/* search the thread who has stopped the process */
/* the thread with TTRCSIG and cursig = signal received on wait() */
for (i = 0; i < nb_k_threads; i++) {
ptr=&pthrdsinfo[i];
if ((ptr->ti_flag & TTRCSIG) && ((ptr->ti_cursig == p->signo) ||
(p->signo == 0)))
break;
}
/* update registers */
if (i != nb_k_threads) {
p->tid = ptr->ti_tid;
readgprs(p,&p->reg[0]);
readsprs(p,&p->reg[NGREG]);
readfprs(p,&p->freg[0]);
for (i=0; i < NGREG+NSYS+MAXFREG; i++) {
p->valid[regword(i)] |= regbit(i);
p->dirty[regword(i)] = 0;
}
}
else {
panic( catgets(scmc_catd, MS_pthread, MSG_772,
"thread with signal not found "));
}
p->sigcode = ptr->ti_code;
return(ptr->ti_tid);
}
#endif /* _THREADS*/
/*
* Get process information from user area.
*/
public getinfo (p, status)
register Process p;
register int status;
{
register int i;
struct user uarea;
Bpinst instruction;
debugger_setpgrp();
p->signo = (status&0177);
p->exitval = ((status >> 8)&0377);
if (p->signo != STOPPED) {
p->status = FINISHED;
p->pid = 0;
p->reg[SYSREGNO(PROGCTR)] = 0;
} else {
p->status = p->signo;
p->signo = p->exitval;
p->exitval = 0;
#if defined ( _THREADS) && !defined (KDBX)
/* update p->sigcode andf registers */
p->tid = gettid(p);
#else
/* p->sigcode = 0 because kernel never inits u_code. It is and
has always been 0. u_code is not a part of the u-block in 4.1
it is part of thread. It is still not used.
*/
p->sigcode = 0;
/* p->sigcode = ptrace(U_READ, p->pid, U_OFSET(urea,u_code), 0); */
for (i = 0; i <= regword(NGREG+NSYS+MAXFREG+1-1); i++) {
p->valid[i] = 0;
p->dirty[i] = 0;
}
#endif /*_THREADS */
pc = reg(SYSREGNO(PROGCTR));
savetty(stdout, &p->ttyinfo);
savetty(stdin, &p->ttyinfo_in);
#if defined ( _THREADS) && !defined (KDBX)
p->sigstatus = getsigstatus(p,p->signo); /* update p->sigstat */
/* in case of trace subcommand curframe has to be updated */
/* if the thread changes, the stack changes so we have to update
frames */
if (tid_running_thread && (p->tid != tid_running_thread))
setcurfunc(whatblock(pc));
tid_running_thread = p->tid;
#else
#define U_SIG U_signal
/* p->sigstatus = ptrace(U_READ, p->pid, U_OFSET(u,u_signal[p->signo]),0); */
p->sigstatus = ptrace(U_READ, p->pid, U_OFSET(uarea,U_SIG[p->signo]),0);
#endif /* _THREADS */
/* make sure we get a valid address from ptrace. */
if (p->sigstatus == -1)
p->sigstatus = 0;
/* if we are stopped because of a trap signal (5) */
if (p->signo == SIGTRAP)
{
/* if this is an active dbx breakpoint */
if (isdbxbp((void *) -1))
{
p->is_bp = true;
}
else
{
/* read the instruction at this address */
iread (&instruction, pc, sizeof (instruction));
/* treat SIGTRAP signals that were not generated
by trap instructions as dbx breakpoints */
if (!is_trap_instruction(instruction))
p->is_bp = true;
#ifdef _THREADS
/* if there are several threads on the same code, we can */
/* receive several SIGTRAP, but the break-point is reset */
if (nb_k_threads >1)
p->is_bp = true;
#endif
}
}
}
#if defined (CMA_THREAD) || defined (K_THREADS)
/* get info about existing threads if any... */
threads(th_get_info, nil);
if(lib_type == KERNEL_THREAD) {
/* update also $ci $mi and $ai */
condition_k(th_get_info,nil);
attribute_k(th_get_info,nil);
mutex_k(th_get_info,nil);
}
#endif /* CMA_THREAD || K_THREADS */
restoretty(stdout, &ttyinfo);
restoretty(stdin, &ttyinfo_in);
}
/*
* Get the current address
*/
getpc(iar)
Word *iar;
{
*iar = reg(SYSREGNO(IAR));
}
/*
* Set process's user area information from given process structure.
*/
public setinfo (p, signo)
register Process p;
int signo;
{
if (signo == DEFSIG) {
/* if we stopped because of a traced signal or a breakpoint
and there is no signal handler
or we are blocking signals */
/* NOTE : if we stopped because of a breakpoint,
sigstatus is set to SIG_IGN. See isbperr */
if (((istraced(p) || p->is_bp)
&& (p->sigstatus == SIG_DFL || p->sigstatus == SIG_IGN))
|| varIsSet("$sigblock")) {
p->signo = 0;
}
} else {
p->signo = signo;
}
setregs(p);
restoretty(stdout, &(p->ttyinfo));
restoretty(stdin, &(p->ttyinfo_in));
debuggee_setpgrp();
#if defined (CMA_THREAD) || defined (K_THREADS)
/* reset current_thread to nil before continuing execution */
current_thread = nil;
#endif /* CMA_THREAD || K_THREADS */
p->is_bp = false;
}
/*
* Set process's user area information from given process structure.
*/
public setregs (p)
register Process p;
{
register int i;
register unsigned *w, mask;
Boolean isdirty = false;
for (i = 0; (i <= NREGBITS) && (!isdirty); i++)
isdirty = (Boolean) p->dirty[i];
if (!isdirty) /* Nothing to do here! */
return;
w = &p->dirty[0];
mask = 1;
for (i = 0; i < NGREG; i++) {
if ((*w & mask) != 0) {
#ifdef _THREADS
/* there is no ptrace to write one register */
writegprs(p, &p->reg[0]);
break;
#else
writereg(p, i, p->reg[i]);
#endif /* _THREADS */
}
mask <<= 1;
if (mask == 0) {
*w = 0;
++w;
mask = 1;
}
}
for (i = NGREG; i < NGREG+NSYS; i++) {
if ((*w & mask) != 0) {
#ifdef _THREADS
/* there is no ptrace to write one register */
writesprs(p, &p->reg[NGREG]);
break;
#else
writereg(p, REGTOSYS(i), p->reg[i]);
#endif /* _THREADS */
}
mask <<= 1;
if (mask == 0) {
*w = 0;
++w;
mask = 1;
}
}
for (i = 0; i < fpregs; i++) {
if ((*w & mask) != 0) {
#ifdef _THREADS
/* there is no ptrace to write one register */
writefprs(p, &p->freg[0]);
break;
#else
writeflreg(p, FREGTOSYS(i), p->freg[i]);
#endif /* _THREADS */
}
mask <<= 1;
if (mask == 0) {
*w = 0;
++w;
mask = 1;
}
}
}
/*
* Return the address associated with the current signal.
* (Plus two since the address points to the beginning of a procedure).
*/
public Address usignal (p)
Process p;
{
Address r;
extern int contsig;
/* if sigblock is on, no signal will go to the program. */
/* So it should not jump to the signal handler. */
if (varIsSet("$sigblock")) {
r = 0;
} else {
r = p->sigstatus;
}
/* If user continued program with a signal, we need to */
/* locate signal handler (if any) of signal provided by user. */
/* This needs to be done no matter if $sigblock is set or not.*/
if (contsig != DEFSIG) {
#ifdef _THREADS
r = getsigstatus(p, contsig);
#else
struct user uarea;
r = ptrace(U_READ, p->pid, U_OFSET(uarea,u_signal[contsig]),0);
#endif /* _THREADS */
}
if (r != 0 and r != 1) {
r += FUNCOFFSET;
}
return r;
}
/*
* Structure for reading and writing by words, but dealing with bytes.
*/
typedef union {
Word pword;
Byte pbyte[sizeof(Word)];
} Pword;
/*
* Read (write) from (to) the process' address space.
* We must deal with ptrace's inability to look anywhere other
* than at a word boundary.
*/
pio (p, op, seg, buff, addr, nbytes)
Process p;
PioOp op;
PioSeg seg;
char *buff;
Address addr;
int nbytes;
{
register int i;
register Address newaddr;
register char *cp;
char *bufend;
Pword w;
Address wordaddr;
int byteoff;
if (p->status != STOPPED) {
error( catgets(scmc_catd, MS_process, MSG_282,
"program is not active"));
}
cp = buff;
newaddr = addr;
wordaddr = (newaddr&WMASK);
if (wordaddr != newaddr) {
w.pword = fetch(p, seg, wordaddr);
for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) {
if (op == PREAD) {
*cp++ = w.pbyte[i];
} else {
w.pbyte[i] = *cp++;
}
nbytes--;
}
if (op == PWRITE) {
store(p, seg, wordaddr, w.pword);
}
newaddr = wordaddr + sizeof(Word);
}
byteoff = (nbytes&(~WMASK));
nbytes -= byteoff;
bufend = cp + nbytes;
while (cp < bufend) {
if (op == PREAD) {
w.pword = fetch(p, seg, newaddr);
for (i = 0; i < sizeof(Word); i++) {
*cp++ = w.pbyte[i];
}
} else {
for (i = 0; i < sizeof(Word); i++) {
w.pbyte[i] = *cp++;
}
store(p, seg, newaddr, w.pword);
}
newaddr += sizeof(Word);
}
if (byteoff > 0) {
w.pword = fetch(p, seg, newaddr);
for (i = 0; i < byteoff; i++) {
if (op == PREAD) {
*cp++ = w.pbyte[i];
} else {
w.pbyte[i] = *cp++;
}
}
if (op == PWRITE) {
store(p, seg, newaddr, w.pword);
}
}
}
/*
* Get a word from a process at the given address.
* The address is assumed to be on a word boundary.
*
* A simple cache scheme is used to avoid redundant ptrace calls
* to the instruction space since it is assumed to be pure.
*
* It is necessary to use a write-through scheme so that
* breakpoints right next to each other don't interfere.
*/
private int nfetchs = 0, nreads = 0, nwrites = 0;
#define CheckTextAddr(addr) \
{ \
if (addr >= 0x20000000) { \
warning( "bad text addr 0x%x", addr); \
return -1; \
} \
}
#define CheckDataAddr(addr) \
{ \
if (addr >= 0x20000000) { \
warning( "bad data addr 0x%x", addr); \
return -1; \
} \
}
private Word fetch (p, seg, addr)
Process p;
PioSeg seg;
register int addr;
{
register CacheWord *wp;
register Word w;
switch (seg) {
case TEXTSEGM:
++nfetchs;
wp = &p->word[cachehash(addr)];
if (addr == 0 or wp->addr != addr) {
/* CheckTextAddr(addr); */
++nreads;
errno = 0;
w = ptrace(I_READ, p->pid, addr, 0, 0);
if ((w == (Address) -1) && (errno == EIO)) {
if (traceback_table)
warning( catgets(scmc_catd, MS_process, MSG_301,
"Invalid trace table. Unreadable instruction \
at address 0x%x"), addr);
else
error( catgets(scmc_catd, MS_process, MSG_291,
"Unreadable instruction at address 0x%x"), addr);
}
wp->addr = addr;
wp->val = w;
} else {
w = wp->val;
}
break;
case DATASEGM:
default:
/* CheckDataAddr(addr); */
w = ptrace(D_READ, p->pid, addr, 0, 0);
/*
* Let the user decide if he wants bad address fetches to abort reading.
if ((w == -1) && (errno == EIO))
error( "Unreadable data at 0x%x", addr);
*/
break;
}
return w;
}
/*
* Put a word into the process' address space at the given address.
* The address is assumed to be on a word boundary.
*/
private store (p, seg, addr, data)
Process p;
PioSeg seg;
int addr;
Word data;
{
register CacheWord *wp;
int w;
switch (seg) {
case TEXTSEGM:
/* CheckTextAddr(addr); */
++nwrites;
wp = &p->word[cachehash(addr)];
wp->addr = addr;
wp->val = data;
errno = 0;
w = ptrace(I_WRITE, p->pid, addr, data, 0);
if ((w == -1) && (errno == EIO))
error( catgets(scmc_catd, MS_process, MSG_294,
"store: could not write instruction at 0x%x"), addr);
break;
case DATASEGM:
default:
/* CheckDataAddr(addr); */
/* User can use assign (dwrite) for assignment to TEXT address */
/* Update cache if we have a value in cache for that address */
/* Don't want to cache it all the time for all addresses. */
wp = &p->word[cachehash(addr)];
if (wp->addr == addr) {
wp->val = data;
}
errno = 0;
w = ptrace(D_WRITE, p->pid, addr, data, 0);
if ((w == -1) && (errno == EIO))
error( catgets(scmc_catd, MS_process, MSG_295,
"store: could not write data at 0x%x"), addr);
break;
/*
The impossible.
default:
panic( "store: bad seg %d", seg);
*/
}
}
/*
* Flush the instruction cache associated with a process.
*/
public cacheflush (p)
Process p;
{
bset0(p->word, sizeof(p->word));
}
public printptraceinfo ()
{
(*rpt_output)(stdout, "%d fetchs, %d reads, %d writes\n",
nfetchs, nreads, nwrites);
}
/*
* Redirect input.
* Assuming this is called from a child, we should be careful to avoid
* (possibly) shared standard I/O buffers.
*/
private infrom (filename)
String filename;
{
Fileid in;
in = open(filename, 0);
if (in == -1) {
write(2, "cannot read ", 11);
write(2, filename, strlen(filename));
write(2, "\n", 1);
_exit(1);
}
fswap(0, in);
}
/*
* Redirect standard output.
* Same assumptions as for "infrom" above.
*/
extern enum redirect { CREATE, APPEND, OVERWRITE } stdout_mode;
extern enum errordirect { STANDARD, SAME_AS_OUT, CREAT, APPND } stderr_mode;
private outto (outfilename, errfilename)
String outfilename, errfilename;
{
Fileid out, err;
int oflag, eflag;
if (!strcmp(objname,outfilename) || !strcmp(errfilename,objname)) {
write(2, "Can't write over running object file!\n", 38);
erecover();
}
if (outfilename != nil) {
if (stdout_mode == APPEND)
oflag = O_RDWR | O_CREAT | O_APPEND;
else
oflag = O_RDWR | O_CREAT | O_TRUNC;
out = open(outfilename, oflag, 0644);
if (out == -1) {
write(2, "cannot write ", 12);
write(2, outfilename, strlen(outfilename));
write(2, "\n", 1);
_exit(1);
}
fswap(1, out);
if (stderr_mode == SAME_AS_OUT)
{
close(2);
dup(1);
}
}
if (errfilename != nil && stderr_mode != SAME_AS_OUT) {
if (stderr_mode == APPND)
eflag = O_RDWR | O_CREAT | O_APPEND;
else
eflag = O_RDWR | O_CREAT | O_TRUNC;
err = open(errfilename, eflag, 0644);
if (err == -1) {
write(2, "cannot write ", 12);
write(2, errfilename, strlen(errfilename));
write(2, "\n", 1);
_exit(1);
}
fswap(2, err);
}
}
reset_pgrp_info()
{
called_debuggee_setpgrp = false;
if (!job_control)
return; /* no job control, no tcsetpgrp! */
save_sig = signal(SIGTTOU, SIG_IGN); /* set up child dbx's pgrp */
tcsetpgrp(0, debugger_pgrp); /* set to debugger's pgrp */
signal(SIGTTOU, save_sig);
}
reset_debuggee_fd(fd)
int fd;
{
if (debuggee_fd == 0) /* only reset this once */
debuggee_fd = fd; /* debugged-parent's STDIN */
}
/*
* debugger_setpgrp() - Set the terminal's process group (pgrp) to the
* debugger's pgrp after saving the debuggee's pgrp.
*/
debugger_setpgrp()
{
pid_t tcpgrp;
if (!job_control)
return; /* no job control, no tcsetpgrp! */
if (called_debuggee_setpgrp) /* call in pairs only */
called_debuggee_setpgrp = false;
else
return;
save_sig = signal(SIGTTOU, SIG_IGN);
tcpgrp = tcgetpgrp(debuggee_fd); /* find out current owner */
/* don't set if in background */
if ((tcpgrp != getppid()) || (multproc != off)
/* don't set if in background */
&& tcpgrp != -1) {
debuggee_pgrp = tcpgrp; /* save debuggee's pgrp */
if (debuggee_pgrp != debugger_pgrp) /* don't set it to what it is now */
tcsetpgrp(debuggee_fd, debugger_pgrp); /* set to debugger's pgrp */
}
signal(SIGTTOU, save_sig);
}
/*
* debuggee_setpgrp() - Set the terminal's process group (pgrp) to the
* debuggee's pgrp.
*/
debuggee_setpgrp()
{
if (!job_control)
return; /* no job control, no tcsetpgrp! */
if (called_debuggee_setpgrp) /* don't call it twice */
return;
else
called_debuggee_setpgrp = true;
if (debuggee_pgrp > 0) {
save_sig = signal(SIGTTOU, SIG_IGN);
if (debugger_pgrp != debuggee_pgrp)
tcsetpgrp(debuggee_fd, debuggee_pgrp); /* set to debuggee's pgrp */
signal(SIGTTOU, save_sig);
}
}