* Removed all Id tags.
[cacao.git] / src / native / jvmti / cacaodbgserver.c
index d05b35645b241acef05c7cb5cfb8a4cf085991a4..1f2f5c9c47c28da11b1e84c096fa7aaaf86f036f 100644 (file)
@@ -1,5 +1,6 @@
+
 /* 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;
 }
 
 
@@ -311,4 +289,5 @@ void cacaodbgserver() {
  * c-basic-offset: 4
  * tab-width: 4
  * End:
+ * vim:noexpandtab:sw=4:ts=4:
  */