1 /* debug-buffer.c -- Trace recording
2    Copyright (C) 1997 John Harper <john@dcs.warwick.ac.uk>
3    $Id$
4 
5    This file is part of Jade.
6 
7    Jade is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    Jade is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with Jade; see the file COPYING.	If not, write to
19    the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
20 
21 #define _GNU_SOURCE
22 
23 #include "repint.h"
24 
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef NEED_MEMORY_H
29 # include <memory.h>
30 #endif
31 
32 struct debug_buf {
33     struct debug_buf *next;
34     char *name;
35     int size, ptr;
36     rep_bool wrapped;
37     char data[1];
38 };
39 
40 #define DB_SIZE(n) (sizeof(struct debug_buf) + (n) - 1)
41 
42 static struct debug_buf *db_chain;
43 
44 void *
rep_db_alloc(char * name,int size)45 rep_db_alloc(char *name, int size)
46 {
47     struct debug_buf *db = rep_alloc(DB_SIZE(size));
48     if(db == NULL)
49     {
50 	perror("create_debug_buf");
51 	abort();
52     }
53     db->name = name;
54     db->size = size;
55     db->ptr = 0;
56     db->wrapped = rep_FALSE;
57     db->next = db_chain;
58     db_chain = db;
59 
60     return db;
61 }
62 
63 void
rep_db_free(void * _db)64 rep_db_free(void *_db)
65 {
66     struct debug_buf *db = _db;
67     struct debug_buf **x = &db_chain;
68     while(*x != NULL)
69     {
70 	if(*x == db)
71 	{
72 	    *x = db->next;
73 	    break;
74 	}
75 	x = &((*x)->next);
76     }
77     rep_free(db);
78 }
79 
80 void
rep_db_vprintf(void * _db,char * fmt,va_list args)81 rep_db_vprintf(void *_db, char *fmt, va_list args)
82 {
83     char buf[256];
84     int length;
85     struct debug_buf *db = _db;
86 
87 #ifdef HAVE_SNPRINTF
88     vsnprintf(buf, sizeof(buf), fmt, args);
89 #else
90     vsprintf(buf, fmt, args);
91 #endif
92     length = strlen(buf);
93     if(length > db->size - db->ptr)
94     {
95 	int before = db->size - db->ptr;
96 	int after = MIN(length - before, db->size - before);
97 	memcpy(db->data + db->ptr, buf, before);
98 	memcpy(db->data, buf + before, after);
99 	db->ptr = after;
100 	db->wrapped = rep_TRUE;
101     }
102     else
103     {
104 	memcpy(db->data + db->ptr, buf, length);
105 	db->ptr += length;
106     }
107 }
108 
109 void
rep_db_printf(void * _db,char * fmt,...)110 rep_db_printf(void *_db, char *fmt, ...)
111 {
112     va_list args;
113     va_start(args, fmt);
114     rep_db_vprintf(_db, fmt, args);
115     va_end(args);
116 }
117 
118 void
rep_db_print_backtrace(void * _db,char * fun)119 rep_db_print_backtrace(void *_db, char *fun)
120 {
121 #if defined(__GNUC__) && ! defined(BROKEN_ALPHA_GCC)
122 
123 #define BT_BASE 1
124 #define BT_DEPTH 8
125 
126     void *stack[BT_BASE+BT_DEPTH];
127     int i;
128 
129     /* It seems that in Linux/egcs-1.1 __builtin_return_address()
130        will segfault when reaching the top of the stack frame.
131        The work-around is to see if we can get the frame address,
132        if so it should be safe to go for the return address. */
133 # define STACK_PROBE(i)						\
134     do {							\
135 	if(i == BT_BASE || stack[i-1] != 0)			\
136 	{							\
137 	    void *frame = __builtin_frame_address(i);		\
138 	    if(frame != 0)					\
139 		stack[i] = __builtin_return_address(i);		\
140 	    else						\
141 		stack[i] = 0;					\
142 	}							\
143 	else							\
144 	    stack[i] = 0;					\
145     } while(0)
146 
147     /* Should be from BT_BASE to BT_BASE+BT_DEPTH-1 */
148     STACK_PROBE(1);  STACK_PROBE(2);  STACK_PROBE(3);  STACK_PROBE(4);
149     STACK_PROBE(5);  STACK_PROBE(6);  STACK_PROBE(7);  STACK_PROBE(8);
150 
151     rep_db_printf(_db, "\nBacktrace in `%s':\n", fun);
152     for(i = BT_BASE; i < BT_BASE+BT_DEPTH && stack[i] != 0; i++)
153     {
154 #ifdef DB_RESOLVE_SYMBOLS
155 	if(stack[i] == 0)
156 	    rep_db_printf(_db, "\t(nil)\n");
157 	else
158 	{
159 	    char *name;
160 	    void *addr;
161 	    if(rep_find_c_symbol(stack[i], &name, &addr))
162 	    {
163 		rep_db_printf(_db, "\t<%s+%d>\n", name,
164 			      ((char *)stack[i]) - ((char *)addr));
165 	    }
166 	    else
167 		rep_db_printf(_db, "\t0x%08lx\n", stack[i]);
168 	}
169 #else
170 	rep_db_printf(_db, "\t0x%08lx\n", stack[i]);
171 #endif
172     }
173 #endif
174 }
175 
176 void *
rep_db_return_address(void)177 rep_db_return_address(void)
178 {
179 #if defined(__GNUC__) && ! defined(BROKEN_ALPHA_GCC)
180     return __builtin_return_address(1);
181 #else
182     return 0;
183 #endif
184 }
185 
186 void
rep_db_spew(void * _db)187 rep_db_spew(void *_db)
188 {
189      struct debug_buf *db = _db;
190 
191      if(db->wrapped || db->ptr > 0)
192      {
193 	 fprintf(stderr, "\nstruct debug_buf %s:\n", db->name);
194 	 if(db->wrapped)
195 	 {
196 	     fwrite(db->data + db->ptr, 1, db->size - db->ptr, stderr);
197 	     fwrite(db->data, 1, db->ptr, stderr);
198 	 }
199 	 else
200 	     fwrite(db->data, 1, db->ptr, stderr);
201      }
202 }
203 
204 void
rep_db_spew_all(void)205 rep_db_spew_all(void)
206 {
207     struct debug_buf *db = db_chain;
208     while(db != NULL)
209     {
210 	rep_db_spew(db);
211 	db = db->next;
212     }
213 }
214 
215 void
rep_db_kill(void)216 rep_db_kill(void)
217 {
218     struct debug_buf *db = db_chain;
219     rep_db_spew_all();
220     db_chain = NULL;
221     while(db != NULL)
222     {
223 	struct debug_buf *next = db->next;
224 	rep_free(db);
225 	db = next;
226     }
227 }
228