/* $Id$ */ /* Running a process and communication */ #include #include #include #include #include #include "ops.h" #include "message.h" #include "position.h" #include "tree.h" #include "file.h" #include "symbol.h" #include "idf.h" #include "scope.h" #include "type.h" #include "expr.h" #include "misc.h" #define MAXARG 128 extern char *strncpy(); extern char *malloc(); extern struct idf *str2idf(); extern char *AObj; extern FILE *db_out; extern int debug; extern char *progname; extern int child_interrupted; extern int interrupted; extern t_lineno currline; static int child_pid; /* process id of child */ static int to_child, from_child; /* file descriptors for communication */ static int child_status; static int restoring; static int fild1[2], fild2[2]; /* pipe file descriptors */ int disable_intr = 1; int db_ss; int stack_offset; void signal_child(); static void catch_sigpipe(); static int stopped(); static int uputm(), ugetm(); static t_addr curr_stop; p_tree run_command; static void ITOBUF(p, l, sz) register char *p; long l; int sz; { register int i; p +=sz; for (i = sz; i > 0; i--) { *--p = l; l >>= 8; } } static long BUFTOI(p, sz) register char *p; int sz; { register long l = 0; register int i; for (i = sz; i>0; i--) { l = (l << 8) | (*p++ & 0377); } return l; } void init_run() { /* take file descriptors so that listing cannot take them */ int i; for (i = 3; i <= 6; i++) close(i); if (pipe(fild1) < 0 || pipe(fild2) < 0 || fild1[0] != 3 || fild2[1] != 6) { fatal("something wrong with file descriptors"); } to_child = fild1[1]; from_child = fild2[0]; child_pid = 0; if (currfile) CurrentScope = currfile->sy_file->f_scope; currline = 0; } extern int errno; start_child(p) p_tree p; { /* start up the process to be debugged and set up communication */ char *argp[MAXARG]; /* argument list */ register p_tree pt = p->t_args[0], pt1 = 0; unsigned int nargs = 1; /* #args */ char *in_redirect = 0; /* standard input redirected */ char *out_redirect = 0; /* standard output redirected */ signal_child(SIGKILL); /* like families in China, this debugger is only allowed one child */ if (p != run_command) { freenode(run_command); run_command = p; } /* first check arguments and redirections and build argument list */ while (pt) { switch(pt->t_oper) { case OP_LINK: pt1 = pt->t_args[1]; pt = pt->t_args[0]; continue; case OP_NAME: if (nargs < (MAXARG-1)) { argp[nargs++] = pt->t_str; } else { error("too many arguments"); return; } break; case OP_INPUT: if (in_redirect) { error("input redirected twice?"); return; } in_redirect = pt->t_str; break; case OP_OUTPUT: if (out_redirect) { error("output redirected twice?"); return; } out_redirect = pt->t_str; break; } if (pt != pt1) pt = pt1; else break; } argp[0] = AObj; argp[nargs] = 0; /* create child process */ child_pid = fork(); if (child_pid < 0) { error("could not create child"); return; } if (child_pid == 0) { /* this is the child process */ close(fild1[1]); close(fild2[0]); signal(SIGINT, SIG_IGN); /* I/O redirection */ if (in_redirect) { int fd; close(0); if ((fd = open(in_redirect, 0)) < 0 || (fd != 0 && dup2(fd, 0) < 0)) { perror(progname); exit(1); } if (fd != 0) { close(fd); } } if (out_redirect) { int fd; close(1); if ((fd = creat(out_redirect, 0666)) < 0 || (fd != 1 && dup2(fd, 1) < 0)) { perror(progname); exit(1); } if (fd != 1) { close(fd); } } /* and run process to be debugged */ execv(AObj, argp); error("could not exec %s", AObj); exit(1); } /* close fild1[0] and fild2[1]; but we want those file descriptors occupied, so we re-occupy them. */ close(fild1[0]); close(fild2[1]); pipe(fild1); /* to occupy file descriptors */ signal(SIGPIPE, catch_sigpipe); { struct message_hdr m; if (! ugetm(&m)) { error("child not responding"); init_run(); return; } curr_stop = BUFTOI(m.m_buf+1, PS); CurrentScope = get_scope_from_addr(curr_stop); } perform_items(); if (! restoring) { int stop_reason = item_addr_actions(curr_stop, 1, 1); if (! stop_reason) (void) send_cont(1); else (void) stopped("stopped", curr_stop, stop_reason); } } void signal_child(sig) { if (child_pid) { kill(child_pid, sig); if (sig == SIGKILL) { wait(&child_status); init_run(); } } } static void catch_sigpipe() { child_pid = 0; } extern int errno; static int do_read(f, p, c) char *p; { int i = read(f, p, c); while (i < 0 && errno == EINTR) i = read(f, p, c); return i; } static int ureceive(p, c) char *p; long c; { int i; char buf[0x1000]; if (! child_pid) { error("no process"); return 0; } if (! p) p = buf; while (c >= 0x1000) { i = do_read(from_child, p, 0x1000); if (i <= 0) { if (i == 0) { child_pid = 0; } else error("read failed"); return 0; } if (p != buf) p += i; c -= i; } while (c > 0) { i = do_read(from_child, p, (int)c); if (i <= 0) { if (i == 0) { child_pid = 0; } else error("read failed"); return 0; } p += i; c -= i; } return 1; } static int usend(p, c) char *p; long c; { int i; if (! child_pid) { error("no process"); return 0; } while (c >= 0x1000) { i = write(to_child, p, 0x1000); if (i < 0) { if (child_pid) error("write failed"); return 0; } p += i; c -= i; } while (c > 0) { i = write(to_child, p, (int)c); if (i < 0) { if (child_pid) error("write failed"); return 0; } p += i; c -= i; } return 1; } static int ugetm(message) struct message_hdr *message; { if (! ureceive((char *) message, (long) sizeof(struct message_hdr))) { return 0; } if (debug) printf("Got %d\n", message->m_type); return 1; } static int uputm(message) struct message_hdr *message; { if (! usend((char *) message, (long) sizeof(struct message_hdr))) { return 0; } if (debug) printf("Sent %d\n", message->m_type); return 1; } static struct message_hdr answer; static int single_stepping; static int stopped(s, a, stop_reason) char *s; /* stop message */ t_addr a; /* address where stopped */ int stop_reason; /* status entry causing the stop */ { p_position pos; if (s && a) { fprintf(db_out, "%s ", s); pos = print_position(a, 1); if (stop_reason) { fprintf(db_out, " (status entry %d)", stop_reason); } fputs("\n", db_out); list_position(pos); handle_displays(); } curr_stop = a; CurrentScope = get_scope_from_addr(a); return 1; } static int could_send(m, stop_message) struct message_hdr *m; { int type; t_addr a; static int level = 0; int child_dead = 0; int stop_reason; level++; for (;;) { stop_reason = 0; if (! child_pid) { error("no process"); return 0; } if (m->m_type & M_DB_RUN) { disable_intr = 0; stack_offset = 0; } if (!child_interrupted && (! uputm(m) || ! ugetm(&answer))) { child_dead = 1; } disable_intr = 1; if ((interrupted || child_interrupted) && ! child_dead) { while (child_interrupted && answer.m_type != M_INTR) { if (! ugetm(&answer)) { child_dead = 1; break; } } if (interrupted && ! child_dead) { level--; if (! level) { child_interrupted = 0; interrupted = 0; return stopped("interrupted", (t_addr) BUFTOI(answer.m_buf+1, PS), 0); } return 1; } } if (child_dead) { wait(&child_status); if (child_status & 0177) { fprintf(db_out, "child died with signal %d\n", child_status & 0177); } else { fprintf(db_out, "child terminated, exit status %d\n", child_status >> 8); } init_run(); level--; return 1; } a = BUFTOI(answer.m_buf+1, PS); type = answer.m_type & 0377; if (type == M_END_SS) type = 0; else if (type == M_OK || type == M_DB_SS) type = 1; else type = 2; if (m->m_type & M_DB_RUN) { /* run command */ CurrentScope = get_scope_from_addr((t_addr) a); if (!(stop_reason = item_addr_actions(a, type, stop_message)) && (type == 1)) { /* no explicit breakpoints at this position. Also, child did not stop because of SETSS or SETSSF, otherwise we would have gotten END_SS. So, continue. */ if ((m->m_type & 0177) != M_CONT) { m->m_type = M_CONT | (m->m_type & M_DB_SS); } continue; } if (type != 0 && single_stepping) { m->m_type = M_CLRSS; if (! uputm(m) || ! ugetm(&answer)) return 0; } single_stepping = 0; } level--; if (stop_message) { return stopped("stopped", a, stop_reason); } return 1; } /*NOTREACHED*/ } static int getbytes(size, from, to, kind, errmess) long size; t_addr from; char *to; int kind; int errmess; { struct message_hdr m; m.m_type = kind; ITOBUF(m.m_buf+1, size, LS); ITOBUF(m.m_buf+LS+1, (long)from, PS); if (! could_send(&m, 0)) { return 0; } switch(answer.m_type) { case M_FAIL: if (errmess) error("could not get value"); return 0; case M_INTR: if (errmess) error("interrupted"); return 0; case M_DATA: return ureceive(to, BUFTOI(answer.m_buf+1, LS)); default: assert(0); } /*NOTREACHED*/ } int get_bytes(size, from, to) long size; t_addr from; char *to; { return getbytes(size, from, to, M_GETBYTES, 1); } int get_string(size, from, to) long size; t_addr from; char *to; { int retval = getbytes(size, from, to, M_GETSTR, 0); to[(int)BUFTOI(answer.m_buf+1, LS)] = 0; return retval; } void set_bytes(size, from, to) long size; char *from; t_addr to; { struct message_hdr m; m.m_type = M_SETBYTES; ITOBUF(m.m_buf+1, size, LS); ITOBUF(m.m_buf+LS+1, (long) to, PS); if (! uputm(&m) || ! usend(from, size) || ! ugetm(&m)) { return; } switch(answer.m_type) { case M_FAIL: error("could not handle this SET request"); break; case M_INTR: error("interrupted"); break; case M_OK: break; default: assert(0); } } t_addr get_dump(globbuf, stackbuf) char **globbuf, **stackbuf; { struct message_hdr m; struct message_hdr *globm, *stackm; long sz; m.m_type = M_DUMP; if (! could_send(&m, 0)) { return 0; } switch(answer.m_type) { case M_FAIL: error("request for DUMP failed"); return 0; case M_INTR: error("interrupted"); return 0; case M_DGLOB: break; default: assert(0); } sz = BUFTOI(answer.m_buf+1, LS); *globbuf = malloc((unsigned) (sz+sizeof(struct message_hdr))); if (! *globbuf || ! ureceive(*globbuf+sizeof(struct message_hdr), sz) || ! ugetm(&m)) { if (*globbuf) free(*globbuf); else error("could not allocate enougn memory"); return 0; } globm = (struct message_hdr *) *globbuf; *globm = answer; assert(m.m_type == M_DSTACK); sz = BUFTOI(m.m_buf+1, LS); *stackbuf = malloc((unsigned) sz+sizeof(struct message_hdr)); if (! *stackbuf || ! ureceive(*stackbuf+sizeof(struct message_hdr), sz)) { free(*globbuf); if (*stackbuf) free(*stackbuf); else error("could not allocate enougn memory"); return 0; } stackm = (struct message_hdr *) *stackbuf; *stackm = m; ITOBUF(globm->m_buf+SP_OFF, BUFTOI(stackm->m_buf+SP_OFF, PS), PS); return BUFTOI(globm->m_buf+PC_OFF, PS); } int put_dump(globbuf, stackbuf) char *globbuf, *stackbuf; { struct message_hdr m; struct message_hdr *globm = (struct message_hdr *) globbuf, *stackm = (struct message_hdr *) stackbuf; stackbuf += sizeof(struct message_hdr); globbuf += sizeof(struct message_hdr); if (! child_pid) { restoring = 1; start_child(run_command); restoring = 0; } return uputm(globm) && usend(globbuf, BUFTOI(globm->m_buf+1, LS)) && uputm(stackm) && usend(stackbuf, BUFTOI(stackm->m_buf+1, LS)) && ugetm(&m) && stopped("restored", BUFTOI(m.m_buf+1, PS), 0); } t_addr * get_EM_regs(level) int level; { struct message_hdr m; static t_addr buf[5]; register t_addr *to = &buf[0]; m.m_type = M_GETEMREGS; ITOBUF(m.m_buf+1, (long) level, LS); if (! could_send(&m, 0)) { return 0; } switch(answer.m_type) { case M_FAIL: error("request for registers failed"); return 0; case M_INTR: error("interrupted"); return 0; case M_GETEMREGS: break; default: assert(0); } *to++ = (t_addr) BUFTOI(answer.m_buf+LB_OFF, PS); *to++ = (t_addr) BUFTOI(answer.m_buf+AB_OFF, PS); *to++ = (t_addr) BUFTOI(answer.m_buf+PC_OFF, PS); *to++ = (t_addr) BUFTOI(answer.m_buf+HP_OFF, PS); *to++ = (t_addr) BUFTOI(answer.m_buf+PC_OFF, PS); return buf; } int set_pc(PC) t_addr PC; { struct message_hdr m; m.m_type = M_SETEMREGS; ITOBUF(m.m_buf+PC_OFF, (long)PC, PS); if (! could_send(&m, 0)) return 0; switch(answer.m_type) { case M_FAIL: error("could not set PC to %lx", (long) PC); return 0; case M_INTR: error("interrupted"); return 0; case M_OK: return 1; default: assert(0); } /*NOTREACHED*/ } int send_cont(stop_message) int stop_message; { struct message_hdr m; m.m_type = (M_CONT | (db_ss ? M_DB_SS : 0)); return could_send(&m, stop_message) && child_pid; } int singlestep(type, count) int type; long count; { struct message_hdr m; m.m_type = (type ? M_SETSSF : M_SETSS) | (db_ss ? M_DB_SS : 0); ITOBUF(m.m_buf+1, count, LS); single_stepping = 1; if (could_send(&m, 1) && child_pid) return 1; single_stepping = 0; return 0; } int set_or_clear_breakpoint(a, type) t_addr a; int type; { struct message_hdr m; m.m_type = type ? M_SETBP : M_CLRBP; ITOBUF(m.m_buf+1, (long) a, PS); if (debug) printf("%s breakpoint at 0x%lx\n", type ? "setting" : "clearing", (long) a); if (child_pid && ! could_send(&m, 0)) { } return 1; } int set_or_clear_trace(start, end, type) t_addr start, end; int type; { struct message_hdr m; m.m_type = type ? M_SETTRACE : M_CLRTRACE; ITOBUF(m.m_buf+1, (long)start, PS); ITOBUF(m.m_buf+PS+1, (long)end, PS); if (debug) printf("%s trace at [0x%lx,0x%lx]\n", type ? "setting" : "clearing", (long) start, (long) end); if (child_pid && ! could_send(&m, 0)) { return 0; } return 1; }