* Removed all Id tags.
[cacao.git] / src / native / jvmti / cacaodbgserver.c
1
2 /* src/native/jvmti/cacaodbgserver.c - contains the cacaodbgserver process. This
3                                        process controls the cacao vm through gdb
4
5    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
6    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
7    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
8    J. Wenninger, Institut f. Computersprachen - TU Wien
9
10    This file is part of CACAO.
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2, or (at
15    your option) any later version.
16
17    This program is distributed in the hope that it will be useful, but
18    WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25    02111-1307, USA.
26
27    Contact: cacao@complang.tuwien.ac.at
28
29    Authors: Martin Platter
30
31    Changes: Edwin Steiner
32             Samuel Vinson
33
34 */
35
36 #include "native/jvmti/cacaodbgserver.h"
37 #include "native/jvmti/cacaodbg.h"
38 #include "native/jvmti/dbg.h"
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <signal.h>
42 #include <sys/wait.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <assert.h>
46 #include <string.h>
47
48 FILE *gdbin, *gdbout; /* file descriptor for gdb pipes */
49
50 struct _pending_brkpt {
51         int brknumber;
52         unsigned long threadid;
53         char* regs;
54 };
55
56 struct _pending_brkpt *pending_brkpts;
57 int pending_brkpts_size;
58
59
60 static void closepipeend (int fd) {
61         if (close(fd) == -1) {
62                 perror("unable to close pipe - ");
63                 exit(1);
64         }
65 }
66
67 /* startgdb *******************************************************************
68
69    starts a gdb session and creates two pipes connection gdb stdout/stdin to
70    gdbin/gdbout
71
72 *******************************************************************************/
73
74 static void startgdb() {
75         char gdbargs[20];
76         int cacao2gdbpipe[2],gdb2cacaopipe[2];
77
78         pipe(cacao2gdbpipe);
79         pipe(gdb2cacaopipe);
80
81         snprintf(gdbargs,20,"--pid=%d",getppid());              
82
83         switch(fork()) {
84         case -1:
85                 fprintf(stderr,"cacaodbgserver: fork error\n");
86                 exit(1);
87         case 0:         
88                 /* child */
89                 closepipeend(gdb2cacaopipe[0]); /* read end */
90                 closepipeend(cacao2gdbpipe[1]); /* write end */
91                 
92                 /* connect stdin of gdb to cacao2gdbpipe */
93                 dup2(cacao2gdbpipe[0],0);
94                 /* connect stdout of gdb to gdb2cacaopipe */
95                 dup2(gdb2cacaopipe[1],1);
96
97                 if (execlp("gdb","gdb","--interpreter=mi" ,gdbargs,(char *) NULL)==-1){
98                         fprintf(stderr,"cacaodbgserver: unable to start gdb\n");
99                         exit(1);
100                 }
101         default:
102                 /* parent */
103                 closepipeend(gdb2cacaopipe[1]); /* write end */
104                 closepipeend(cacao2gdbpipe[0]); /* read end */
105
106                 gdbin = fdopen(gdb2cacaopipe[0],"r");
107                 gdbout = fdopen(cacao2gdbpipe[1],"w");
108         }
109
110 }
111
112
113 #define SENDCMD(CMD) \
114         fprintf(gdbout,"%s",CMD); \
115         fflush(gdbout); \
116
117
118 static void getgdboutput(char *inbuf,int buflen) {
119         int i=0;
120         inbuf[0]='\0';
121         do {
122                 i += strlen(&inbuf[i]);
123                 if (fgets(&inbuf[i],buflen-i,gdbin)==NULL) {
124                         perror("cacaodbgserver: ");
125                         exit(1);
126                 }
127         } while(!(strncmp(OUTPUTEND,&inbuf[i],OUTPUTENDSIZE)==0));
128 }
129
130
131 /* dataevaluate ***************************************************************
132
133    evaluates expr returning long in gdb and returns the result 
134
135 *******************************************************************************/
136
137 static unsigned long dataevaluate(char *expr) {
138         char *match, inbuf[160];
139
140         fprintf(gdbout,"-data-evaluate-expression %s\n",expr); 
141         fflush(gdbout); 
142
143         getgdboutput(inbuf,160);
144         if ((match=strstr(inbuf,DATAEVALUATE)) == NULL) {
145                 fprintf(stderr,"dataevaluate: no matching value\n");
146                 return -1;
147         }
148         return strtoll(&match[strlen(DATAEVALUATE)], NULL, 16);
149 }
150
151
152 /* commonbreakpointhandler *****************************************************
153
154    called by gdb and hard coded breakpoint handler
155
156 *******************************************************************************/
157
158 static bool commonbreakpointhandler(char* sigbuf, int sigtrap) {
159         int numberofbreakpoints, i;
160         char tmp[INBUFLEN], *match;
161         unsigned long addr;
162
163         if ((match=strstr(sigbuf,SIGADDR))==NULL) {
164                 fprintf(stderr,"commonbreakpointhandler: no matching address(%s)\n",
165                                 sigbuf);
166                 return true;
167         }
168
169         addr = strtoll(&match[strlen(SIGADDR)],NULL,16);
170         if (sigtrap) addr--;
171
172
173         numberofbreakpoints = (int)dataevaluate("dbgcom->jvmtibrkpt.num");
174
175         i = -1;
176         do {
177                 i++; 
178                 snprintf(tmp,INBUFLEN,"dbgcom->jvmtibrkpt.brk[%d].addr",i);
179         } while ((i<numberofbreakpoints) && (dataevaluate(tmp) != addr));
180
181         assert(i<numberofbreakpoints);
182
183         /* handle system breakpoints */
184         switch(i) {
185         case SETSYSBRKPT:
186                 /* add a breakpoint */
187                 fprintf(gdbout,"break *0x%lx\n",dataevaluate("dbgcom->brkaddr"));
188                 fflush(gdbout);
189                 getgdboutput(tmp,INBUFLEN);
190                 break;
191         case CACAODBGSERVERQUIT:
192                 SENDCMD("-gdb-exit\n");
193                 return false;
194         default:
195                 /* other breakpoints -> call jvmti_cacao_generic_breakpointhandler 
196                    in the cacao vm */
197                 fprintf(gdbout,"call jvmti_cacao_generic_breakpointhandler(%d)\n",i);
198                 fflush(gdbout);
199                 getgdboutput(tmp,INBUFLEN);
200         }
201         SENDCMD(CONTINUE);
202         getgdboutput(tmp,INBUFLEN);
203         return true;
204 }
205
206 /* controlloop ****************************************************************
207
208    this function controls the gdb behaviour
209
210 *******************************************************************************/
211
212 static void controlloop() {
213         char inbuf[INBUFLEN], *match;
214         bool running = true;
215
216         pending_brkpts_size = 5;
217         pending_brkpts = malloc(sizeof(struct _pending_brkpt)*pending_brkpts_size);
218
219         getgdboutput(inbuf,INBUFLEN);   /* read gdb welcome message */
220
221         SENDCMD("handle SIGSEGV SIGPWR SIGXCPU SIGBUS noprint nostop\n");
222         getgdboutput(inbuf,INBUFLEN);
223
224         SENDCMD("print dbgcom\n");
225         getgdboutput(inbuf,INBUFLEN);
226
227         SENDCMD(CONTINUE);
228         getgdboutput(inbuf,INBUFLEN);
229
230         while(running) {
231                 getgdboutput(inbuf,INBUFLEN);
232
233                 if ((match=strstr(inbuf,HCSIGTRAP))!=NULL) {
234                         running = commonbreakpointhandler(match,1);
235                         continue;
236                         }
237
238                 if ((match=strstr(inbuf,GDBBREAKPOINT))!=NULL) {
239                         running = commonbreakpointhandler(match,0);
240                         continue;
241                 }
242
243                 if (strstr(inbuf,EXITEDNORMALLY) != NULL) {
244                         /* quit gdb */
245                         SENDCMD ("-gdb-exit");
246                         running = false;
247                         continue;
248                 }
249                         
250                 if ((inbuf[0]!=LOGSTREAMOUTPUT) && (inbuf[0]!=CONSOLESTREAMOUTPUT)) {
251                         fprintf(stderr,"gdbin not handled %s\n",inbuf);
252                         fprintf(gdbout,"bt\n");
253                         fflush(gdbout);
254                         fprintf(stderr,"not handled 1\n");
255                         fflush(stderr);
256                         getgdboutput(inbuf,INBUFLEN);
257                         fprintf(stderr,"gdbin: %s\n",inbuf);
258                         SENDCMD("-gdb-exit\n");
259                         return;
260                 }
261         }
262
263         free(pending_brkpts);
264 }
265
266 /* main (cacaodbgserver) ******************************************************
267
268    main function for cacaodbgserver process.
269
270 *******************************************************************************/
271
272 int main(int argc, char **argv) {
273         startgdb();
274
275         controlloop();
276
277         return 0;
278 }
279
280
281 /*
282  * These are local overrides for various environment variables in Emacs.
283  * Please do not remove this and leave it at the end of the file, where
284  * Emacs will automagically detect them.
285  * ---------------------------------------------------------------------
286  * Local variables:
287  * mode: c
288  * indent-tabs-mode: t
289  * c-basic-offset: 4
290  * tab-width: 4
291  * End:
292  * vim:noexpandtab:sw=4:ts=4:
293  */