blob: 090f304f9d84782c7ee7d0543b90647a0a161dad [file] [log] [blame]
sewardj1cf558c2005-04-25 01:36:56 +00001
2/*--------------------------------------------------------------------*/
3/*--- Debug (not-for-user) logging; also vprintf. m_debuglog.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
10 Copyright (C) 2000-2005 Julian Seward
11 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
31
32/* Performs low-level debug logging that can safely run immediately
33 after startup. To minimise the dependencies on any other parts of
34 the system, the only place the debug output may go is file
35 descriptor 2 (stderr).
36*/
37/* This is the first-initialised module in the entire system!
38 Therefore it is CRITICAL that it does not depend on any other code
39 running first. Hence only the following very limited includes. We
40 cannot depend (directly or indirectly) on any dynamic memory
41 allocation facilities, nor on the m_libc facilities, since the
42 latter depend on this module. DO NOT MESS WITH THESE INCLUDES
43 UNLESS YOU ARE 100% CERTAIN YOU UNDERSTAND THE CONSEQUENCES.
44*/
45/* This module is also different from all others in the sense that it
46 is linked into both stage1 and stage2.
47*/
48#include "basic_types.h" /* basic types */
49#include "pub_core_debuglog.h" /* our own iface */
50
51
52/*------------------------------------------------------------*/
53/*--- Stuff to make us completely independent. ---*/
54/*------------------------------------------------------------*/
55
56/* ----- x86-linux specifics ----- */
57
58#if VG_PLATFORM == x86-linux
59
60static UInt local_sys_write_stderr ( HChar* buf, Int n )
61{
62 UInt __res;
63 __asm__ volatile ("int $0x80"
64 : "=a" (__res)
65 : "0" (4), /* __NR_write */
66 "b" (2), /* stderr */
67 "c" (buf),
68 "d" (n) );
69 if (__res < 0)
70 __res = -1;
71 return __res;
72}
73
74static UInt local_sys_getpid ( void )
75{
76 UInt __res;
77 __asm__ volatile ("int $0x80"
78 : "=a" (__res)
79 : "0" (20) /* __NR_getpid */);
80 return __res;
81}
82
83
84#else
85#error Unknown VG_PLATFORM
86#endif
87
88
89/* ----- generic ----- */
90
91/* strlen, so we don't need m_libc */
92static Int local_strlen ( const HChar* str )
93{
94 Int i = 0;
95 while (str[i] != 0) i++;
96 return i;
97}
98
99static HChar local_toupper ( HChar c )
100{
101 if (c >= 'a' && c <= 'z')
102 return c + ('A' - 'a');
103 else
104 return c;
105}
106
107/* Emit buf[0 .. n-1] to stderr. Unfortunately platform-specific.
108*/
109static void emit ( HChar* buf, Int n )
110{
111 if (n >= 1)
112 (void)local_sys_write_stderr(buf, n);
113}
114
115
116/*------------------------------------------------------------*/
117/*--- A simple, generic, vprintf implementation. ---*/
118/*------------------------------------------------------------*/
119
120/* -----------------------------------------------
121 Distantly derived from:
122
123 vprintf replacement for Checker.
124 Copyright 1993, 1994, 1995 Tristan Gingold
125 Written September 1993 Tristan Gingold
126 Tristan Gingold, 8 rue Parmentier, F-91120 PALAISEAU, FRANCE
127
128 (Checker itself was GPL'd.)
129 ----------------------------------------------- */
130
131/* Some flags. */
132#define VG_MSG_SIGNED 1 /* The value is signed. */
133#define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */
134#define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */
135#define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */
136#define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */
137
138
139/* Copy a string into the buffer. */
140static
141UInt myvprintf_str ( void(*send)(HChar,void*),
142 void* send_arg2,
143 Int flags,
144 Int width,
145 HChar* str,
146 Bool capitalise )
147{
148# define MAYBE_TOUPPER(ch) (capitalise ? local_toupper(ch) : (ch))
149 UInt ret = 0;
150 Int i, extra;
151 Int len = local_strlen(str);
152
153 if (width == 0) {
154 ret += len;
155 for (i = 0; i < len; i++)
156 send(MAYBE_TOUPPER(str[i]), send_arg2);
157 return ret;
158 }
159
160 if (len > width) {
161 ret += width;
162 for (i = 0; i < width; i++)
163 send(MAYBE_TOUPPER(str[i]), send_arg2);
164 return ret;
165 }
166
167 extra = width - len;
168 if (flags & VG_MSG_LJUSTIFY) {
169 ret += extra;
170 for (i = 0; i < extra; i++)
171 send(' ', send_arg2);
172 }
173 ret += len;
174 for (i = 0; i < len; i++)
175 send(MAYBE_TOUPPER(str[i]), send_arg2);
176 if (!(flags & VG_MSG_LJUSTIFY)) {
177 ret += extra;
178 for (i = 0; i < extra; i++)
179 send(' ', send_arg2);
180 }
181
182# undef MAYBE_TOUPPER
183 return ret;
184}
185
186
187/* Write P into the buffer according to these args:
188 * If SIGN is true, p is a signed.
189 * BASE is the base.
190 * If WITH_ZERO is true, '0' must be added.
191 * WIDTH is the width of the field.
192 */
193static
194UInt myvprintf_int64 ( void(*send)(HChar,void*),
195 void* send_arg2,
196 Int flags,
197 Int base,
198 Int width,
199 ULong p )
200{
201 HChar buf[40];
202 Int ind = 0;
203 Int i, nc = 0;
204 Bool neg = False;
205 HChar* digits = "0123456789ABCDEF";
206 UInt ret = 0;
207
208 if (base < 2 || base > 16)
209 return ret;
210
211 if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
212 p = - (Long)p;
213 neg = True;
214 }
215
216 if (p == 0)
217 buf[ind++] = '0';
218 else {
219 while (p > 0) {
220 if (flags & VG_MSG_COMMA && 10 == base &&
221 0 == (ind-nc) % 3 && 0 != ind)
222 {
223 buf[ind++] = ',';
224 nc++;
225 }
226 buf[ind++] = digits[p % base];
227 p /= base;
228 }
229 }
230
231 if (neg)
232 buf[ind++] = '-';
233
234 if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
235 for(; ind < width; ind++) {
236 /* vg_assert(ind < 39); */
237 if (ind > 39) {
238 buf[39] = 0;
239 break;
240 }
241 buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
242 }
243 }
244
245 /* Reverse copy to buffer. */
246 ret += ind;
247 for (i = ind -1; i >= 0; i--) {
248 send(buf[i], send_arg2);
249 }
250 if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
251 for(; ind < width; ind++) {
252 ret++;
253 /* Never pad with zeroes on RHS -- changes the value! */
254 send(' ', send_arg2);
255 }
256 }
257 return ret;
258}
259
260
261/* A simple vprintf(). */
262/* EXPORTED */
263UInt
264VG_(debugLog_vprintf) (
265 void(*send)(HChar,void*),
266 void* send_arg2,
267 const HChar* format,
268 va_list vargs
269)
270{
271 UInt ret = 0;
272 Int i;
273 Int flags;
274 Int width;
275 Bool is_long;
276
277 /* We assume that vargs has already been initialised by the
278 caller, using va_start, and that the caller will similarly
279 clean up with va_end.
280 */
281
282 for (i = 0; format[i] != 0; i++) {
283 if (format[i] != '%') {
284 send(format[i], send_arg2);
285 ret++;
286 continue;
287 }
288 i++;
289 /* A '%' has been found. Ignore a trailing %. */
290 if (format[i] == 0)
291 break;
292 if (format[i] == '%') {
293 /* `%%' is replaced by `%'. */
294 send('%', send_arg2);
295 ret++;
296 continue;
297 }
298 flags = 0;
299 is_long = False;
300 width = 0; /* length of the field. */
301 if (format[i] == '(') {
302 flags |= VG_MSG_PAREN;
303 i++;
304 }
305 /* If ',' follows '%', commas will be inserted. */
306 if (format[i] == ',') {
307 flags |= VG_MSG_COMMA;
308 i++;
309 }
310 /* If '-' follows '%', justify on the left. */
311 if (format[i] == '-') {
312 flags |= VG_MSG_LJUSTIFY;
313 i++;
314 }
315 /* If '0' follows '%', pads will be inserted. */
316 if (format[i] == '0') {
317 flags |= VG_MSG_ZJUSTIFY;
318 i++;
319 }
320 /* Compute the field length. */
321 while (format[i] >= '0' && format[i] <= '9') {
322 width *= 10;
323 width += format[i++] - '0';
324 }
325 while (format[i] == 'l') {
326 i++;
327 is_long = True;
328 }
329
330 switch (format[i]) {
331 case 'd': /* %d */
332 flags |= VG_MSG_SIGNED;
333 if (is_long)
334 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
335 (ULong)(va_arg (vargs, Long)));
336 else
337 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
338 (ULong)(va_arg (vargs, Int)));
339 break;
340 case 'u': /* %u */
341 if (is_long)
342 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
343 (ULong)(va_arg (vargs, ULong)));
344 else
345 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
346 (ULong)(va_arg (vargs, UInt)));
347 break;
348 case 'p': /* %p */
349 ret += 2;
350 send('0',send_arg2);
351 send('x',send_arg2);
352 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
353 (ULong)((UWord)va_arg (vargs, void *)));
354 break;
355 case 'x': /* %x */
356 if (is_long)
357 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
358 (ULong)(va_arg (vargs, ULong)));
359 else
360 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
361 (ULong)(va_arg (vargs, UInt)));
362 break;
363 case 'c': /* %c */
364 ret++;
365 send(va_arg (vargs, int), send_arg2);
366 break;
367 case 's': case 'S': { /* %s */
368 char *str = va_arg (vargs, char *);
369 if (str == (char*) 0) str = "(null)";
370 ret += myvprintf_str(send, send_arg2,
371 flags, width, str, format[i]=='S');
372 break;
373 }
374// case 'y': { /* %y - print symbol */
375// Char buf[100];
376// Char *cp = buf;
377// Addr a = va_arg(vargs, Addr);
378//
379// if (flags & VG_MSG_PAREN)
380// *cp++ = '(';
381// if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) {
382// if (flags & VG_MSG_PAREN) {
383// cp += VG_(strlen)(cp);
384// *cp++ = ')';
385// *cp = '\0';
386// }
387// ret += myvprintf_str(send, send_arg2, flags, width, buf, 0);
388// }
389// break;
390// }
391 default:
392 break;
393 }
394 }
395 return ret;
396}
397
398
399/*------------------------------------------------------------*/
400/*--- Debuglog stuff. ---*/
401/*------------------------------------------------------------*/
402
403/* Only print messages whose stated level is less than or equal to
404 this. By default, it makes this entire subsystem silent. */
405
406static Int loglevel = 0;
407
408/* EXPORTED */
409/* Module startup. */
410void VG_(debugLog_startup) ( Int level, HChar* who )
411{
412 if (level < 0) level = 0;
413 if (level > 10) level = 10;
414 loglevel = level;
415 VG_(debugLog)(1, "debuglog",
416 "DebugLog system started by %s, "
417 "level %d logging requested\n",
418 who, loglevel);
419}
420
421/* ------------ */
422
423typedef
424 struct {
425 HChar buf[100];
426 Int n;
427 }
428 printf_buf;
429
430static void add_to_buf ( HChar c, void* p )
431{
432 printf_buf* buf = (printf_buf*)p;
433
434 if (buf->n >= 100-10 /*paranoia*/ ) {
435 emit( buf->buf, local_strlen(buf->buf) );
436 buf->n = 0;
437 buf->buf[buf->n] = 0;
438 }
439 buf->buf[buf->n++] = c;
440 buf->buf[buf->n] = 0;
441}
442
443/* Send a logging message. Nothing is output unless 'level'
444 is <= the current loglevel. */
445/* EXPORTED */
446__attribute__((format(__printf__, 3, 4)))
447void VG_(debugLog) ( Int level, const HChar* modulename,
448 const HChar* format, ... )
449{
450 UInt ret, pid;
sewardja5ebfa92005-04-25 02:04:54 +0000451 Int indent;
sewardj1cf558c2005-04-25 01:36:56 +0000452 va_list vargs;
453 printf_buf buf;
sewardja5ebfa92005-04-25 02:04:54 +0000454
sewardj1cf558c2005-04-25 01:36:56 +0000455
456 if (level > loglevel)
457 return;
458
sewardja5ebfa92005-04-25 02:04:54 +0000459 indent = 1*level - 1;
460 if (indent < 1) indent = 1;
461
sewardj1cf558c2005-04-25 01:36:56 +0000462 buf.n = 0;
463 buf.buf[0] = 0;
464 pid = local_sys_getpid();
sewardja5ebfa92005-04-25 02:04:54 +0000465 (void)myvprintf_str ( add_to_buf, &buf, 0, 2, "--", False );
466 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)pid );
467 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000468 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)level );
sewardja5ebfa92005-04-25 02:04:54 +0000469 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000470 (void)myvprintf_str ( add_to_buf, &buf, 0, 8, (HChar*)modulename, False );
sewardja5ebfa92005-04-25 02:04:54 +0000471 (void)myvprintf_str ( add_to_buf, &buf, 0, indent, "", False );
sewardj1cf558c2005-04-25 01:36:56 +0000472
473 va_start(vargs,format);
474
475 ret = VG_(debugLog_vprintf) ( add_to_buf, &buf, format, vargs );
476
477 if (buf.n > 0) {
478 emit( buf.buf, local_strlen(buf.buf) );
479 }
480
481 va_end(vargs);
482}
483
484
485
486/*--------------------------------------------------------------------*/
487/*--- end m_debuglog.c ---*/
488/*--------------------------------------------------------------------*/