- Document where the stabs information can be found, so that someone
[wine] / debugger / source.c
1 /*
2  * File source.c - source file handling for internal debugger.
3  *
4  * Copyright (C) 1997, Eric Youngdale.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_MMAN_H
27 #include <sys/mman.h>
28 #endif
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <limits.h>
32 #include <string.h>
33 #include <unistd.h>
34 #ifndef PATH_MAX
35 #define PATH_MAX MAX_PATH
36 #endif
37
38 #include "debugger.h"
39
40 struct searchlist
41 {
42   char * path;
43   struct searchlist * next;
44 };
45
46
47 struct open_filelist
48 {
49   char                  * path;
50   char                  * real_path;
51   struct open_filelist  * next;
52   unsigned int            size;
53   signed int              nlines;
54   unsigned int          * linelist;
55 };
56
57 static struct open_filelist * ofiles;
58
59 static struct searchlist * listhead;
60 static char DEBUG_current_sourcefile[PATH_MAX];
61 static int DEBUG_start_sourceline = -1;
62 static int DEBUG_end_sourceline = -1;
63
64 void
65 DEBUG_ShowDir(void)
66 {
67   struct searchlist * sl;
68
69   DEBUG_Printf(DBG_CHN_MESG,"Search list :\n");
70   for(sl = listhead; sl; sl = sl->next)
71     {
72       DEBUG_Printf(DBG_CHN_MESG, "\t%s\n", sl->path);
73     }
74   DEBUG_Printf(DBG_CHN_MESG, "\n");
75 }
76
77 void
78 DEBUG_AddPath(const char * path)
79 {
80   struct searchlist * sl;
81
82   sl = (struct searchlist *) DBG_alloc(sizeof(struct searchlist));
83   if( sl == NULL )
84     {
85       return;
86     }
87
88   sl->next = listhead;
89   sl->path = DBG_strdup(path);
90   listhead = sl;
91 }
92
93 void
94 DEBUG_NukePath(void)
95 {
96   struct searchlist * sl;
97   struct searchlist * nxt;
98
99   for(sl = listhead; sl; sl = nxt)
100     {
101       nxt = sl->next;
102       DBG_free(sl->path);
103       DBG_free(sl);
104     }
105
106   listhead = NULL;
107 }
108
109 static
110 int
111 DEBUG_DisplaySource(char * sourcefile, int start, int end)
112 {
113   char                        * addr;
114   char                          buffer[1024];
115   int                           fd;
116   int                           i;
117   struct open_filelist        * ol;
118   int                           nlines;
119   char                        * basename = NULL;
120   char                        * pnt;
121   int                           rtn;
122   struct searchlist           * sl;
123   struct stat                   statbuf;
124   int                           status;
125   char                          tmppath[PATH_MAX];
126
127   /*
128    * First see whether we have the file open already.  If so, then
129    * use that, otherwise we have to try and open it.
130    */
131   for(ol = ofiles; ol; ol = ol->next)
132     {
133       if( strcmp(ol->path, sourcefile) == 0 )
134         {
135           break;
136         }
137     }
138
139   if( ol == NULL )
140     {
141       /*
142        * Try again, stripping the path from the opened file.
143        */
144       basename = strrchr(sourcefile, '\\' );
145       if ( !basename )
146           basename = strrchr(sourcefile, '/' );
147       if ( !basename )
148           basename = sourcefile;
149       else
150           basename++;
151
152       for(ol = ofiles; ol; ol = ol->next)
153         {
154           if( strcmp(ol->path, basename) == 0 )
155             {
156               break;
157             }
158         }
159
160     }
161
162   if( ol == NULL )
163     {
164       /*
165        * Crapola.  We need to try and open the file.
166        */
167       status = stat(sourcefile, &statbuf);
168       if( status != -1 )
169         {
170           strcpy(tmppath, sourcefile);
171         }
172       else if( (status = stat(basename, &statbuf)) != -1 )
173         {
174           strcpy(tmppath, basename);
175         }
176       else
177         {
178           for(sl = listhead; sl; sl = sl->next)
179             {
180               strcpy(tmppath, sl->path);
181               if( tmppath[strlen(tmppath)-1] != '/' )
182                 {
183                   strcat(tmppath, "/");
184                 }
185               /*
186                * Now append the base file name.
187                */
188               strcat(tmppath, basename);
189
190               status = stat(tmppath, &statbuf);
191               if( status != -1 )
192                 {
193                   break;
194                 }
195             }
196
197           if( sl == NULL )
198             {
199                 if (DEBUG_InteractiveP)
200                 {
201                     char zbuf[256];
202                     /*
203                      * Still couldn't find it.  Ask user for path to add.
204                      */
205                     sprintf(zbuf, "Enter path to file '%s': ", sourcefile);
206                     DEBUG_ReadLine(zbuf, tmppath, sizeof(tmppath));
207
208                     if( tmppath[strlen(tmppath)-1] == '\n' )
209                     {
210                         tmppath[strlen(tmppath)-1] = '\0';
211                     }
212
213                     if( tmppath[strlen(tmppath)-1] != '/' )
214                     {
215                         strcat(tmppath, "/");
216                     }
217                     /*
218                      * Now append the base file name.
219                      */
220                     strcat(tmppath, basename);
221
222                     status = stat(tmppath, &statbuf);
223                 }
224               if( status == -1 )
225                 {
226                   /*
227                    * OK, I guess the user doesn't really want to see it
228                    * after all.
229                    */
230                   ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
231                   ol->path = DBG_strdup(sourcefile);
232                   ol->real_path = NULL;
233                   ol->next = ofiles;
234                   ol->nlines = 0;
235                   ol->linelist = NULL;
236                   ofiles = ol;
237                   DEBUG_Printf(DBG_CHN_MESG,"Unable to open file %s\n", tmppath);
238                   return FALSE;
239                 }
240             }
241         }
242       /*
243        * Create header for file.
244        */
245       ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
246       ol->path = DBG_strdup(sourcefile);
247       ol->real_path = DBG_strdup(tmppath);
248       ol->next = ofiles;
249       ol->nlines = 0;
250       ol->linelist = NULL;
251       ol->size = statbuf.st_size;
252       ofiles = ol;
253
254       /*
255        * Now open and map the file.
256        */
257       fd = open(tmppath, O_RDONLY);
258       if( fd == -1 )
259         {
260           return FALSE;
261         }
262
263       addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
264       if( addr == (char *) -1 )
265         {
266           return FALSE;
267         }
268
269       /*
270        * Now build up the line number mapping table.
271        */
272       ol->nlines = 1;
273       pnt = addr;
274       while(pnt < addr + ol->size )
275         {
276           if( *pnt++ == '\n' )
277             {
278               ol->nlines++;
279             }
280         }
281
282       ol->nlines++;
283       ol->linelist = (unsigned int*) DBG_alloc( ol->nlines * sizeof(unsigned int) );
284
285       nlines = 0;
286       pnt = addr;
287       ol->linelist[nlines++] = 0;
288       while(pnt < addr + ol->size )
289         {
290           if( *pnt++ == '\n' )
291             {
292               ol->linelist[nlines++] = pnt - addr;
293             }
294         }
295       ol->linelist[nlines++] = pnt - addr;
296
297     }
298   else
299     {
300       /*
301        * We know what the file is, we just need to reopen it and remap it.
302        */
303       fd = open(ol->real_path, O_RDONLY);
304       if( fd == -1 )
305         {
306           return FALSE;
307         }
308
309       addr = mmap(0, ol->size, PROT_READ, MAP_PRIVATE, fd, 0);
310       if( addr == (char *) -1 )
311         {
312           return FALSE;
313         }
314     }
315
316   /*
317    * All we need to do is to display the source lines here.
318    */
319   rtn = FALSE;
320   for(i=start - 1; i <= end - 1; i++)
321     {
322       if( i < 0 || i >= ol->nlines - 1)
323         {
324           continue;
325         }
326
327       rtn = TRUE;
328       memset(&buffer, 0, sizeof(buffer));
329       if( ol->linelist[i+1] != ol->linelist[i] )
330         {
331           memcpy(&buffer, addr + ol->linelist[i],
332                  (ol->linelist[i+1] - ol->linelist[i]) - 1);
333         }
334       DEBUG_Printf(DBG_CHN_MESG,"%d\t%s\n", i + 1,  buffer);
335     }
336
337   munmap(addr, ol->size);
338   close(fd);
339
340   return rtn;
341
342 }
343
344 void
345 DEBUG_List(struct list_id * source1, struct list_id * source2,
346                          int delta)
347 {
348   int    end;
349   int    rtn;
350   int    start;
351   char * sourcefile;
352
353   /*
354    * We need to see what source file we need.  Hopefully we only have
355    * one specified, otherwise we might as well punt.
356    */
357   if( source1 != NULL
358       && source2 != NULL
359       && source1->sourcefile != NULL
360       && source2->sourcefile != NULL
361       && strcmp(source1->sourcefile, source2->sourcefile) != 0 )
362     {
363       DEBUG_Printf(DBG_CHN_MESG, "Ambiguous source file specification.\n");
364       return;
365     }
366
367   sourcefile = NULL;
368   if( source1 != NULL && source1->sourcefile != NULL )
369     {
370       sourcefile = source1->sourcefile;
371     }
372
373   if( sourcefile == NULL
374       && source2 != NULL
375       && source2->sourcefile != NULL )
376     {
377       sourcefile = source2->sourcefile;
378     }
379
380   if( sourcefile == NULL )
381     {
382       sourcefile = (char *) &DEBUG_current_sourcefile;
383     }
384
385   if( sourcefile == NULL )
386     {
387       DEBUG_Printf(DBG_CHN_MESG, "No source file specified.\n");
388       return;
389     }
390
391   /*
392    * Now figure out the line number range to be listed.
393    */
394   start = -1;
395   end = -1;
396
397   if( source1 != NULL )
398     {
399       start = source1->line;
400     }
401
402   if( source2 != NULL )
403     {
404       end = source2->line;
405     }
406
407   if( start == -1 && end == -1 )
408     {
409       if( delta < 0 )
410         {
411           end = DEBUG_start_sourceline;
412           start = end + delta;
413         }
414       else
415         {
416           start = DEBUG_end_sourceline;
417           end = start + delta;
418         }
419     }
420   else if( start == -1 )
421     {
422       start = end + delta;
423     }
424   else if (end == -1)
425     {
426       end = start + delta;
427     }
428
429   /*
430    * Now call this function to do the dirty work.
431    */
432   rtn = DEBUG_DisplaySource(sourcefile, start, end);
433
434   if( sourcefile != (char *) &DEBUG_current_sourcefile )
435     {
436       strcpy(DEBUG_current_sourcefile, sourcefile);
437     }
438   DEBUG_start_sourceline = start;
439   DEBUG_end_sourceline = end;
440 }
441
442 DBG_ADDR DEBUG_LastDisassemble={0,0};
443
444 BOOL DEBUG_DisassembleInstruction(DBG_ADDR *addr)
445 {
446    char         ch;
447    BOOL         ret = TRUE;
448
449    DEBUG_PrintAddress(addr, DEBUG_CurrThread->dbg_mode, TRUE);
450    DEBUG_Printf(DBG_CHN_MESG, ": ");
451    if (!DEBUG_READ_MEM_VERBOSE((void*)DEBUG_ToLinear(addr), &ch, sizeof(ch))) {
452       DEBUG_Printf(DBG_CHN_MESG, "-- no code --");
453       ret = FALSE;
454    } else {
455       DEBUG_Disasm(addr, TRUE);
456    }
457    DEBUG_Printf(DBG_CHN_MESG,"\n");
458    return ret;
459 }
460
461 void
462 DEBUG_Disassemble(const DBG_VALUE *xstart,const DBG_VALUE *xend,int offset)
463 {
464   int i;
465   DBG_ADDR      last;
466   DBG_VALUE     end,start;
467
468   if (xstart) {
469     start = *xstart;
470     DEBUG_GrabAddress(&start, TRUE);
471   }
472   if (xend) {
473     end = *xend;
474     DEBUG_GrabAddress(&end, TRUE);
475   }
476   if (!xstart && !xend) {
477     last = DEBUG_LastDisassemble;
478     if (!last.seg && !last.off)
479       DEBUG_GetCurrentAddress( &last );
480
481     for (i=0;i<offset;i++)
482       if (!DEBUG_DisassembleInstruction(&last)) break;
483     DEBUG_LastDisassemble = last;
484     return;
485   }
486   last = start.addr;
487   if (!xend) {
488     for (i=0;i<offset;i++)
489       if (!DEBUG_DisassembleInstruction(&last)) break;
490     DEBUG_LastDisassemble = last;
491     return;
492   }
493   while (last.off <= end.addr.off)
494     if (!DEBUG_DisassembleInstruction(&last)) break;
495   DEBUG_LastDisassemble = last;
496   return;
497 }