+
/* src/native/jvmti/cacaodbgserver.c - contains the cacaodbgserver process. This
- process controls the debuggee.
+ process controls the cacao vm through gdb
Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
Authors: Martin Platter
- Changes:
-
-
- $Id: cacao.c,v 3.165 2006/01/03 23:44:38 twisti Exp $
+ Changes: Edwin Steiner
+ Samuel Vinson
*/
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <semaphore.h>
-#include <sys/msg.h>
-#include <linux/user.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+FILE *gdbin, *gdbout; /* file descriptor for gdb pipes */
-/* getchildprocptrace *********************************************************
+struct _pending_brkpt {
+ int brknumber;
+ unsigned long threadid;
+ char* regs;
+};
- Get data count number of bytes from address addr for child process address
- space. Requested data is stored in the array pointed to by ptr.
+struct _pending_brkpt *pending_brkpts;
+int pending_brkpts_size;
-*******************************************************************************/
-static void getchildprocptrace (char *ptr, void* addr, int cnt) {
- int i, longcnt;
- long *p = (long*) ptr;
-
- longcnt = cnt/sizeof(long);
- for (i=0; i<longcnt; i++) {
- p[i]=GETMEM(debuggee,addr);
- if (p[i]==-1)
- perror("cacaodbgserver process: getchildprocptrace: ");
- addr+=sizeof(long);
+
+static void closepipeend (int fd) {
+ if (close(fd) == -1) {
+ perror("unable to close pipe - ");
+ exit(1);
}
- longcnt = GETMEM(debuggee,addr);
- memcpy(ptr,&longcnt,cnt%sizeof(long));
}
-/* contchild ******************************************************************
+/* startgdb *******************************************************************
- Helper function to continue child process.
+ starts a gdb session and creates two pipes connection gdb stdout/stdin to
+ gdbin/gdbout
*******************************************************************************/
-static bool contchild(int signal) {
- /* get lock for running state */
- sem_wait(&workingdata_lock);
- fprintf(stderr,"cacaodbgserver: contchild called (hastostop: %d)\n",cdbgshmem->hastostop);
- if(cdbgshmem->hastostop < 1) {
- fprintf(stderr,"cacaodbgserver: going to continue child\n");
- CONT(debuggee,signal);
- cdbgshmem->hastostop = 0;
- cdbgshmem->running=true;
- /* release lock for running state */
- sem_post(&workingdata_lock);
- return true;
- } else {
- sem_post(&workingdata_lock);
- return false;
- }
-}
-/* msgqsendevent *******************************************************************
+static void startgdb() {
+ char gdbargs[20];
+ int cacao2gdbpipe[2],gdb2cacaopipe[2];
- sends an event notification to the jdwp/debugger process through the
- message queue
+ pipe(cacao2gdbpipe);
+ pipe(gdb2cacaopipe);
-*******************************************************************************/
-static void msgqsendevent(basic_event *ev) {
- ev->mtype = MSGQDEBUGGER;
-
- if (-1 == msgsnd(msgqid, ev, sizeof(basic_event), 0)) {
- perror("cacaodbgserver process: cacaodbglisten send error: ");
+ snprintf(gdbargs,20,"--pid=%d",getppid());
+
+ switch(fork()) {
+ case -1:
+ fprintf(stderr,"cacaodbgserver: fork error\n");
exit(1);
+ case 0:
+ /* child */
+ closepipeend(gdb2cacaopipe[0]); /* read end */
+ closepipeend(cacao2gdbpipe[1]); /* write end */
+
+ /* connect stdin of gdb to cacao2gdbpipe */
+ dup2(cacao2gdbpipe[0],0);
+ /* connect stdout of gdb to gdb2cacaopipe */
+ dup2(gdb2cacaopipe[1],1);
+
+ if (execlp("gdb","gdb","--interpreter=mi" ,gdbargs,(char *) NULL)==-1){
+ fprintf(stderr,"cacaodbgserver: unable to start gdb\n");
+ exit(1);
+ }
+ default:
+ /* parent */
+ closepipeend(gdb2cacaopipe[1]); /* write end */
+ closepipeend(cacao2gdbpipe[0]); /* read end */
+
+ gdbin = fdopen(gdb2cacaopipe[0],"r");
+ gdbout = fdopen(cacao2gdbpipe[1],"w");
}
+
}
-/* waitloop *******************************************************************
- waits and handles signals from debuggee/child process
+#define SENDCMD(CMD) \
+ fprintf(gdbout,"%s",CMD); \
+ fflush(gdbout); \
+
+
+static void getgdboutput(char *inbuf,int buflen) {
+ int i=0;
+ inbuf[0]='\0';
+ do {
+ i += strlen(&inbuf[i]);
+ if (fgets(&inbuf[i],buflen-i,gdbin)==NULL) {
+ perror("cacaodbgserver: ");
+ exit(1);
+ }
+ } while(!(strncmp(OUTPUTEND,&inbuf[i],OUTPUTENDSIZE)==0));
+}
+
+
+/* dataevaluate ***************************************************************
+
+ evaluates expr returning long in gdb and returns the result
*******************************************************************************/
-static void waitloop() {
- int status,retval,signal;
- void* ip;
- basic_event ev;
+static unsigned long dataevaluate(char *expr) {
+ char *match, inbuf[160];
- fprintf(stderr,"waitloop\n");
- fflush (stderr);
+ fprintf(gdbout,"-data-evaluate-expression %s\n",expr);
+ fflush(gdbout);
- while (true) {
- retval = wait(&status);
+ getgdboutput(inbuf,160);
+ if ((match=strstr(inbuf,DATAEVALUATE)) == NULL) {
+ fprintf(stderr,"dataevaluate: no matching value\n");
+ return -1;
+ }
+ return strtoll(&match[strlen(DATAEVALUATE)], NULL, 16);
+}
- fprintf(stderr,"cacaodbgserver: waitloop we got something to do\n");
- if (retval == -1) {
- fprintf(stderr,"error in waitloop\n");
- perror("cacaodbgserver process: waitloop: ");
- exit(1);
- }
- if (retval != debuggee) {
- fprintf(stderr,"cacaodbgserver got signal from process other then debuggee\n");
- exit(1);
- }
-
- if (WIFEXITED(status)) {
- /* generate event VMDeath */
- ev.signal = SIGQUIT;
- ev.ip = NULL;
- msgqsendevent(&ev);
- return;
- }
-
- if (WIFSTOPPED(status)) {
- signal = WSTOPSIG(status);
-
- /* ignore SIGSEGV, SIGPWR, SIGBUS and SIGXCPU for now.
- todo: in future this signals can be used to detect Garbage
- Collection Start/Finish or NullPointerException Events */
- if ((signal == SIGSEGV) || (signal == SIGPWR) ||
- (signal == SIGBUS) || (signal == SIGXCPU)) {
- fprintf(stderr,"cacaodbgserver: ignore internal signal (%d)\n",signal);
- contchild(signal);
- continue;
- }
+/* commonbreakpointhandler *****************************************************
- if (signal == SIGABRT) {
- fprintf(stderr,"cacaodbgserver: got SIGABRT from debugee - exit\n");
- exit(1);
- }
+ called by gdb and hard coded breakpoint handler
- ip = getip(debuggee);
- ip--; /* EIP has already been incremented */
- fprintf(stderr,"got signal: %d IP %X\n",signal,ip);
-
- sem_wait(&workingdata_lock);
- cdbgshmem->running = false;
- cdbgshmem->hastostop = 1;
- sem_post(&workingdata_lock);
-
- if (signal==SIGUSR2) {
- fprintf(stderr,"SIGUSR2 - debuggee has stopped by jdwp process\n");
- return;
- }
-
- ev.signal = signal;
- ev.ip = ip;
- msgqsendevent(&ev);
- return;
- }
-
- fprintf(stderr,"wait not handled(child not exited or stopped)\n");
- fprintf(stderr,"retval: %d status: %d\n",retval,status);
+*******************************************************************************/
+
+static bool commonbreakpointhandler(char* sigbuf, int sigtrap) {
+ int numberofbreakpoints, i;
+ char tmp[INBUFLEN], *match;
+ unsigned long addr;
+
+ if ((match=strstr(sigbuf,SIGADDR))==NULL) {
+ fprintf(stderr,"commonbreakpointhandler: no matching address(%s)\n",
+ sigbuf);
+ return true;
}
+
+ addr = strtoll(&match[strlen(SIGADDR)],NULL,16);
+ if (sigtrap) addr--;
+
+
+ numberofbreakpoints = (int)dataevaluate("dbgcom->jvmtibrkpt.num");
+
+ i = -1;
+ do {
+ i++;
+ snprintf(tmp,INBUFLEN,"dbgcom->jvmtibrkpt.brk[%d].addr",i);
+ } while ((i<numberofbreakpoints) && (dataevaluate(tmp) != addr));
+
+ assert(i<numberofbreakpoints);
+
+ /* handle system breakpoints */
+ switch(i) {
+ case SETSYSBRKPT:
+ /* add a breakpoint */
+ fprintf(gdbout,"break *0x%lx\n",dataevaluate("dbgcom->brkaddr"));
+ fflush(gdbout);
+ getgdboutput(tmp,INBUFLEN);
+ break;
+ case CACAODBGSERVERQUIT:
+ SENDCMD("-gdb-exit\n");
+ return false;
+ default:
+ /* other breakpoints -> call jvmti_cacao_generic_breakpointhandler
+ in the cacao vm */
+ fprintf(gdbout,"call jvmti_cacao_generic_breakpointhandler(%d)\n",i);
+ fflush(gdbout);
+ getgdboutput(tmp,INBUFLEN);
+ }
+ SENDCMD(CONTINUE);
+ getgdboutput(tmp,INBUFLEN);
+ return true;
}
-/* ptraceloop *****************************************************************
+/* controlloop ****************************************************************
- this function handles the ptrace request from the jdwp/debugger process.
+ this function controls the gdb behaviour
*******************************************************************************/
-void ptraceloop() {
- bool contdebuggee=false;
- ptrace_request pt;
- ptrace_reply *buffer;
- int size;
- struct user_regs_struct *regs;
-
- fprintf(stderr,"ptraceloop\n");
- fflush (stderr);
- while (!contdebuggee) {
- if (-1 == msgrcv(msgqid, &pt, sizeof(ptrace_request), MSGQPTRACESND, 0))
- perror("cacaodbgserver process: cacaodbglisten receive error: ");
-
- switch(pt.kind){
- case PTCONT:
- /* continue debuggee process */
- size= sizeof(ptrace_reply);
- buffer =(ptrace_reply*) MNEW(char,size);
-
- contdebuggee = contchild(pt.data);
-
- buffer->mtype = MSGQPTRACERCV;
- buffer->successful=true;
- buffer->datasize=0;
- break;
- case PTPEEKDATA:
- /* get memory content from the debuggee process */
- size= sizeof(ptrace_reply)+pt.data;
- buffer =(ptrace_reply*) MNEW(char,size);
-
- buffer->mtype = MSGQPTRACERCV;
- buffer->datasize = size-sizeof(ptrace_reply);
-
- fprintf(stderr,"getchildprocptrace: pid %d get %p - %p cnt: %d (buffer %p buffer->data %p)\n",
- debuggee, pt.addr,pt.addr+pt.data, buffer->datasize,buffer, buffer->data);
- fflush(stderr);
+static void controlloop() {
+ char inbuf[INBUFLEN], *match;
+ bool running = true;
- getchildprocptrace(buffer->data,pt.addr,buffer->datasize);
- break;
- case PTSETBRK:
- size= sizeof(ptrace_reply)+sizeof(long);
- buffer =(ptrace_reply*) MNEW(char,size);
+ pending_brkpts_size = 5;
+ pending_brkpts = malloc(sizeof(struct _pending_brkpt)*pending_brkpts_size);
- /* set new breakpoint */
- buffer->mtype = MSGQPTRACERCV;
- buffer->successful=true;
- buffer->datasize=sizeof(long);
-
- setbrk(debuggee,pt.addr, (long*)(buffer->data));
- break;
- case PTDELBRK:
- /* delete breakpoint */
- size= sizeof(ptrace_reply);
- buffer =(ptrace_reply*) MNEW(char,size);
-
- DISABLEBRK(debuggee,pt.ldata,pt.addr);
-
- buffer->mtype = MSGQPTRACERCV;
- buffer->successful=true;
- buffer->datasize=0;
- break;
- case PTGETREG:
- /* get registers */
- size= sizeof(ptrace_reply)+sizeof(struct user_regs_struct);
- buffer =(ptrace_reply*) MNEW(char,size);
- regs=buffer->data;
-
- GETREGS(debuggee,*regs);
-
- buffer->mtype = MSGQPTRACERCV;
- buffer->successful=true;
- buffer->datasize=sizeof(struct user_regs_struct);
- break;
- default:
- fprintf(stderr,"unkown ptrace request %d\n",pt.kind);
- exit(1);
+ getgdboutput(inbuf,INBUFLEN); /* read gdb welcome message */
+
+ SENDCMD("handle SIGSEGV SIGPWR SIGXCPU SIGBUS noprint nostop\n");
+ getgdboutput(inbuf,INBUFLEN);
+
+ SENDCMD("print dbgcom\n");
+ getgdboutput(inbuf,INBUFLEN);
+
+ SENDCMD(CONTINUE);
+ getgdboutput(inbuf,INBUFLEN);
+
+ while(running) {
+ getgdboutput(inbuf,INBUFLEN);
+
+ if ((match=strstr(inbuf,HCSIGTRAP))!=NULL) {
+ running = commonbreakpointhandler(match,1);
+ continue;
+ }
+
+ if ((match=strstr(inbuf,GDBBREAKPOINT))!=NULL) {
+ running = commonbreakpointhandler(match,0);
+ continue;
}
- if (-1 == msgsnd(msgqid, buffer, size, 0)) {
- perror("cacaodbgserver process: cacaodbglisten send error: ");
- exit(1);
+ if (strstr(inbuf,EXITEDNORMALLY) != NULL) {
+ /* quit gdb */
+ SENDCMD ("-gdb-exit");
+ running = false;
+ continue;
+ }
+
+ if ((inbuf[0]!=LOGSTREAMOUTPUT) && (inbuf[0]!=CONSOLESTREAMOUTPUT)) {
+ fprintf(stderr,"gdbin not handled %s\n",inbuf);
+ fprintf(gdbout,"bt\n");
+ fflush(gdbout);
+ fprintf(stderr,"not handled 1\n");
+ fflush(stderr);
+ getgdboutput(inbuf,INBUFLEN);
+ fprintf(stderr,"gdbin: %s\n",inbuf);
+ SENDCMD("-gdb-exit\n");
+ return;
}
- MFREE(buffer,char,size);
}
+
+ free(pending_brkpts);
}
-/* cacaodbgserver *************************************************************
+/* main (cacaodbgserver) ******************************************************
- waits for eventes from and issues ptrace calls to debuggee/child process
+ main function for cacaodbgserver process.
*******************************************************************************/
-void cacaodbgserver() {
- fprintf(stderr,"cacaodbgserver started\n");
- fflush(stderr);
- while(true) {
- /* wait until debuggee process gets stopped
- and inform debugger process */
- waitloop();
- /* give the debugger process the opportunity to issue ptrace calls */
- ptraceloop();
- /* ptraceloop returns after a PTRACE_CONT call has been issued */
- }
+int main(int argc, char **argv) {
+ startgdb();
+
+ controlloop();
+
+ return 0;
}
* c-basic-offset: 4
* tab-width: 4
* End:
+ * vim:noexpandtab:sw=4:ts=4:
*/