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