blob: a5782803867cde463e5e1aa3edaa933b8dd9fb32 [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*/
sewardj1cf558c2005-04-25 01:36:56 +000045
njnc7561b92005-06-19 01:24:32 +000046/* This module is also notable because it is linked into both
47 stage1 and stage2. */
48
49#include "pub_core_basics.h" /* basic types */
50#include "pub_core_debuglog.h" /* our own iface */
sewardj1cf558c2005-04-25 01:36:56 +000051
52/*------------------------------------------------------------*/
53/*--- Stuff to make us completely independent. ---*/
54/*------------------------------------------------------------*/
55
56/* ----- x86-linux specifics ----- */
57
sewardj21c6d0f2005-05-02 10:33:44 +000058#if defined(VGP_x86_linux)
sewardj1cf558c2005-04-25 01:36:56 +000059
60static UInt local_sys_write_stderr ( HChar* buf, Int n )
61{
62 UInt __res;
tom311400b2005-04-27 08:58:53 +000063 __asm__ volatile (
sewardjd485d692005-04-28 09:40:53 +000064 "movl $4, %%eax\n" /* %eax = __NR_write */
65 "movl $2, %%edi\n" /* %edi = stderr */
66 "movl %1, %%ecx\n" /* %ecx = buf */
67 "movl %2, %%edx\n" /* %edx = n */
68 "pushl %%ebx\n"
69 "movl %%edi, %%ebx\n"
70 "int $0x80\n" /* write(stderr, buf, n) */
71 "popl %%ebx\n"
72 "movl %%eax, %0\n" /* __res = eax */
tom311400b2005-04-27 08:58:53 +000073 : "=mr" (__res)
sewardjd4d203b2005-04-27 23:17:48 +000074 : "g" (buf), "g" (n)
sewardjd485d692005-04-28 09:40:53 +000075 : "eax", "edi", "ecx", "edx"
sewardjd4d203b2005-04-27 23:17:48 +000076 );
sewardj1cf558c2005-04-25 01:36:56 +000077 if (__res < 0)
78 __res = -1;
79 return __res;
80}
81
82static UInt local_sys_getpid ( void )
83{
84 UInt __res;
tom311400b2005-04-27 08:58:53 +000085 __asm__ volatile (
86 "movl $20, %%eax\n" /* set %eax = __NR_getpid */
87 "int $0x80\n" /* getpid() */
88 "movl %%eax, %0\n" /* set __res = eax */
sewardjd4d203b2005-04-27 23:17:48 +000089 : "=mr" (__res)
90 :
91 : "eax" );
sewardj1cf558c2005-04-25 01:36:56 +000092 return __res;
93}
94
sewardj21c6d0f2005-05-02 10:33:44 +000095#elif defined(VGP_amd64_linux)
sewardj601371a2005-04-25 16:21:17 +000096
97static UInt local_sys_write_stderr ( HChar* buf, Int n )
98{
tomc6121862005-04-27 09:23:02 +000099 UInt __res;
100 __asm__ volatile (
101 "movq $1, %%rax\n" /* set %rax = __NR_write */
102 "movq $2, %%rdi\n" /* set %rdi = stderr */
103 "movq %1, %%rsi\n" /* set %rsi = buf */
sewardjd4d203b2005-04-27 23:17:48 +0000104 "movl %2, %%edx\n" /* set %edx = n */
tomc6121862005-04-27 09:23:02 +0000105 "syscall\n" /* write(stderr, buf, n) */
106 "movl %%eax, %0\n" /* set __res = %eax */
107 : "=mr" (__res)
108 : "g" (buf), "g" (n)
109 : "rax", "rdi", "rsi", "rdx" );
110 if (__res < 0)
111 __res = -1;
112 return __res;
sewardj601371a2005-04-25 16:21:17 +0000113}
114
115static UInt local_sys_getpid ( void )
116{
tomc6121862005-04-27 09:23:02 +0000117 UInt __res;
118 __asm__ volatile (
119 "movq $39, %%rax\n" /* set %rax = __NR_getpid */
120 "syscall\n" /* getpid() */
121 "movl %%eax, %0\n" /* set __res = %eax */
122 : "=mr" (__res)
123 :
124 : "rax" );
125 return __res;
sewardj601371a2005-04-25 16:21:17 +0000126}
sewardj1cf558c2005-04-25 01:36:56 +0000127
cerion85665ca2005-06-20 15:51:07 +0000128#elif defined(VGP_ppc32_linux)
129
130static UInt local_sys_write_stderr ( HChar* buf, Int n )
131{
132 UInt __res;
133 __asm__ volatile (
134 "li %%r0,4\n\t" /* set %r0 = __NR_write */
135 "li %%r3,2\n\t" /* set %r3 = stderr */
136 "mr %%r4,%1\n\t" /* set %r4 = buf */
137 "mr %%r5,%2\n\t" /* set %r5 = n */
138 "sc\n\t" /* write(stderr, buf, n) */
139 "mr %0,%%r3\n" /* set __res = r3 */
140 : "=mr" (__res)
141 : "g" (buf), "g" (n)
142 : "r0", "r3", "r4", "r5" );
143 if (__res < 0)
144 __res = -1;
145 return __res;
146}
147
148static UInt local_sys_getpid ( void )
149{
150 UInt __res;
151 __asm__ volatile (
152 "li %%r0,20\n" /* set %r0 = __NR_getpid */
153 "\tsc\n" /* getpid() */
154 "\tmr %0,%%r3\n" /* set __res = r3 */
155 : "=mr" (__res)
156 :
157 : "r0" );
158 return __res;
159}
160
sewardj1cf558c2005-04-25 01:36:56 +0000161#else
sewardj21c6d0f2005-05-02 10:33:44 +0000162# error Unknown platform
sewardj1cf558c2005-04-25 01:36:56 +0000163#endif
164
165
166/* ----- generic ----- */
167
168/* strlen, so we don't need m_libc */
169static Int local_strlen ( const HChar* str )
170{
171 Int i = 0;
172 while (str[i] != 0) i++;
173 return i;
174}
175
176static HChar local_toupper ( HChar c )
177{
178 if (c >= 'a' && c <= 'z')
179 return c + ('A' - 'a');
180 else
181 return c;
182}
183
184/* Emit buf[0 .. n-1] to stderr. Unfortunately platform-specific.
185*/
186static void emit ( HChar* buf, Int n )
187{
188 if (n >= 1)
189 (void)local_sys_write_stderr(buf, n);
190}
191
192
193/*------------------------------------------------------------*/
194/*--- A simple, generic, vprintf implementation. ---*/
195/*------------------------------------------------------------*/
196
197/* -----------------------------------------------
198 Distantly derived from:
199
200 vprintf replacement for Checker.
201 Copyright 1993, 1994, 1995 Tristan Gingold
202 Written September 1993 Tristan Gingold
203 Tristan Gingold, 8 rue Parmentier, F-91120 PALAISEAU, FRANCE
204
205 (Checker itself was GPL'd.)
206 ----------------------------------------------- */
207
208/* Some flags. */
209#define VG_MSG_SIGNED 1 /* The value is signed. */
210#define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */
211#define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */
212#define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */
213#define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */
214
215
216/* Copy a string into the buffer. */
217static
218UInt myvprintf_str ( void(*send)(HChar,void*),
219 void* send_arg2,
220 Int flags,
221 Int width,
222 HChar* str,
223 Bool capitalise )
224{
225# define MAYBE_TOUPPER(ch) (capitalise ? local_toupper(ch) : (ch))
226 UInt ret = 0;
227 Int i, extra;
228 Int len = local_strlen(str);
229
230 if (width == 0) {
231 ret += len;
232 for (i = 0; i < len; i++)
233 send(MAYBE_TOUPPER(str[i]), send_arg2);
234 return ret;
235 }
236
237 if (len > width) {
238 ret += width;
239 for (i = 0; i < width; i++)
240 send(MAYBE_TOUPPER(str[i]), send_arg2);
241 return ret;
242 }
243
244 extra = width - len;
245 if (flags & VG_MSG_LJUSTIFY) {
246 ret += extra;
247 for (i = 0; i < extra; i++)
248 send(' ', send_arg2);
249 }
250 ret += len;
251 for (i = 0; i < len; i++)
252 send(MAYBE_TOUPPER(str[i]), send_arg2);
253 if (!(flags & VG_MSG_LJUSTIFY)) {
254 ret += extra;
255 for (i = 0; i < extra; i++)
256 send(' ', send_arg2);
257 }
258
259# undef MAYBE_TOUPPER
260 return ret;
261}
262
263
264/* Write P into the buffer according to these args:
265 * If SIGN is true, p is a signed.
266 * BASE is the base.
267 * If WITH_ZERO is true, '0' must be added.
268 * WIDTH is the width of the field.
269 */
270static
271UInt myvprintf_int64 ( void(*send)(HChar,void*),
272 void* send_arg2,
273 Int flags,
274 Int base,
275 Int width,
276 ULong p )
277{
278 HChar buf[40];
279 Int ind = 0;
280 Int i, nc = 0;
281 Bool neg = False;
282 HChar* digits = "0123456789ABCDEF";
283 UInt ret = 0;
284
285 if (base < 2 || base > 16)
286 return ret;
287
288 if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
289 p = - (Long)p;
290 neg = True;
291 }
292
293 if (p == 0)
294 buf[ind++] = '0';
295 else {
296 while (p > 0) {
297 if (flags & VG_MSG_COMMA && 10 == base &&
298 0 == (ind-nc) % 3 && 0 != ind)
299 {
300 buf[ind++] = ',';
301 nc++;
302 }
303 buf[ind++] = digits[p % base];
304 p /= base;
305 }
306 }
307
308 if (neg)
309 buf[ind++] = '-';
310
311 if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
312 for(; ind < width; ind++) {
313 /* vg_assert(ind < 39); */
314 if (ind > 39) {
315 buf[39] = 0;
316 break;
317 }
318 buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
319 }
320 }
321
322 /* Reverse copy to buffer. */
323 ret += ind;
324 for (i = ind -1; i >= 0; i--) {
325 send(buf[i], send_arg2);
326 }
327 if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
328 for(; ind < width; ind++) {
329 ret++;
330 /* Never pad with zeroes on RHS -- changes the value! */
331 send(' ', send_arg2);
332 }
333 }
334 return ret;
335}
336
337
338/* A simple vprintf(). */
339/* EXPORTED */
340UInt
341VG_(debugLog_vprintf) (
342 void(*send)(HChar,void*),
343 void* send_arg2,
344 const HChar* format,
345 va_list vargs
346)
347{
348 UInt ret = 0;
349 Int i;
350 Int flags;
351 Int width;
352 Bool is_long;
353
354 /* We assume that vargs has already been initialised by the
355 caller, using va_start, and that the caller will similarly
356 clean up with va_end.
357 */
358
359 for (i = 0; format[i] != 0; i++) {
360 if (format[i] != '%') {
361 send(format[i], send_arg2);
362 ret++;
363 continue;
364 }
365 i++;
366 /* A '%' has been found. Ignore a trailing %. */
367 if (format[i] == 0)
368 break;
369 if (format[i] == '%') {
njn02bc4b82005-05-15 17:28:26 +0000370 /* '%%' is replaced by '%'. */
sewardj1cf558c2005-04-25 01:36:56 +0000371 send('%', send_arg2);
372 ret++;
373 continue;
374 }
375 flags = 0;
376 is_long = False;
377 width = 0; /* length of the field. */
378 if (format[i] == '(') {
379 flags |= VG_MSG_PAREN;
380 i++;
381 }
382 /* If ',' follows '%', commas will be inserted. */
383 if (format[i] == ',') {
384 flags |= VG_MSG_COMMA;
385 i++;
386 }
387 /* If '-' follows '%', justify on the left. */
388 if (format[i] == '-') {
389 flags |= VG_MSG_LJUSTIFY;
390 i++;
391 }
392 /* If '0' follows '%', pads will be inserted. */
393 if (format[i] == '0') {
394 flags |= VG_MSG_ZJUSTIFY;
395 i++;
396 }
397 /* Compute the field length. */
398 while (format[i] >= '0' && format[i] <= '9') {
399 width *= 10;
400 width += format[i++] - '0';
401 }
402 while (format[i] == 'l') {
403 i++;
404 is_long = True;
405 }
406
407 switch (format[i]) {
408 case 'd': /* %d */
409 flags |= VG_MSG_SIGNED;
410 if (is_long)
411 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
412 (ULong)(va_arg (vargs, Long)));
413 else
414 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
415 (ULong)(va_arg (vargs, Int)));
416 break;
417 case 'u': /* %u */
418 if (is_long)
419 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
420 (ULong)(va_arg (vargs, ULong)));
421 else
422 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
423 (ULong)(va_arg (vargs, UInt)));
424 break;
425 case 'p': /* %p */
426 ret += 2;
427 send('0',send_arg2);
428 send('x',send_arg2);
429 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
430 (ULong)((UWord)va_arg (vargs, void *)));
431 break;
432 case 'x': /* %x */
433 if (is_long)
434 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
435 (ULong)(va_arg (vargs, ULong)));
436 else
437 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
438 (ULong)(va_arg (vargs, UInt)));
439 break;
440 case 'c': /* %c */
441 ret++;
442 send(va_arg (vargs, int), send_arg2);
443 break;
444 case 's': case 'S': { /* %s */
445 char *str = va_arg (vargs, char *);
446 if (str == (char*) 0) str = "(null)";
447 ret += myvprintf_str(send, send_arg2,
448 flags, width, str, format[i]=='S');
449 break;
450 }
451// case 'y': { /* %y - print symbol */
452// Char buf[100];
453// Char *cp = buf;
454// Addr a = va_arg(vargs, Addr);
455//
456// if (flags & VG_MSG_PAREN)
457// *cp++ = '(';
458// if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) {
459// if (flags & VG_MSG_PAREN) {
460// cp += VG_(strlen)(cp);
461// *cp++ = ')';
462// *cp = '\0';
463// }
464// ret += myvprintf_str(send, send_arg2, flags, width, buf, 0);
465// }
466// break;
467// }
468 default:
469 break;
470 }
471 }
472 return ret;
473}
474
475
476/*------------------------------------------------------------*/
477/*--- Debuglog stuff. ---*/
478/*------------------------------------------------------------*/
479
480/* Only print messages whose stated level is less than or equal to
481 this. By default, it makes this entire subsystem silent. */
482
483static Int loglevel = 0;
484
sewardj1cf558c2005-04-25 01:36:56 +0000485/* Module startup. */
sewardj10759312005-05-30 23:52:47 +0000486/* EXPORTED */
sewardj1cf558c2005-04-25 01:36:56 +0000487void VG_(debugLog_startup) ( Int level, HChar* who )
488{
489 if (level < 0) level = 0;
490 if (level > 10) level = 10;
491 loglevel = level;
492 VG_(debugLog)(1, "debuglog",
493 "DebugLog system started by %s, "
494 "level %d logging requested\n",
495 who, loglevel);
496}
497
sewardj10759312005-05-30 23:52:47 +0000498/* Get the logging threshold level, as set by the most recent call to
499 VG_(debugLog_startup), or zero if there have been no such calls so
500 far. */
501/* EXPORTED */
502Int VG_(debugLog_getLevel) ( void )
503{
504 return loglevel;
505}
506
507
sewardj1cf558c2005-04-25 01:36:56 +0000508/* ------------ */
509
510typedef
511 struct {
512 HChar buf[100];
513 Int n;
514 }
515 printf_buf;
516
517static void add_to_buf ( HChar c, void* p )
518{
519 printf_buf* buf = (printf_buf*)p;
520
521 if (buf->n >= 100-10 /*paranoia*/ ) {
522 emit( buf->buf, local_strlen(buf->buf) );
523 buf->n = 0;
524 buf->buf[buf->n] = 0;
525 }
526 buf->buf[buf->n++] = c;
527 buf->buf[buf->n] = 0;
528}
529
530/* Send a logging message. Nothing is output unless 'level'
531 is <= the current loglevel. */
532/* EXPORTED */
533__attribute__((format(__printf__, 3, 4)))
534void VG_(debugLog) ( Int level, const HChar* modulename,
535 const HChar* format, ... )
536{
537 UInt ret, pid;
sewardja5ebfa92005-04-25 02:04:54 +0000538 Int indent;
sewardj1cf558c2005-04-25 01:36:56 +0000539 va_list vargs;
540 printf_buf buf;
sewardja5ebfa92005-04-25 02:04:54 +0000541
sewardj1cf558c2005-04-25 01:36:56 +0000542
543 if (level > loglevel)
544 return;
545
sewardjd85feff2005-04-25 02:37:56 +0000546 indent = 2*level - 1;
sewardja5ebfa92005-04-25 02:04:54 +0000547 if (indent < 1) indent = 1;
548
sewardj1cf558c2005-04-25 01:36:56 +0000549 buf.n = 0;
550 buf.buf[0] = 0;
551 pid = local_sys_getpid();
sewardja5ebfa92005-04-25 02:04:54 +0000552 (void)myvprintf_str ( add_to_buf, &buf, 0, 2, "--", False );
553 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)pid );
554 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000555 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)level );
sewardja5ebfa92005-04-25 02:04:54 +0000556 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000557 (void)myvprintf_str ( add_to_buf, &buf, 0, 8, (HChar*)modulename, False );
sewardja5ebfa92005-04-25 02:04:54 +0000558 (void)myvprintf_str ( add_to_buf, &buf, 0, indent, "", False );
sewardj1cf558c2005-04-25 01:36:56 +0000559
560 va_start(vargs,format);
561
562 ret = VG_(debugLog_vprintf) ( add_to_buf, &buf, format, vargs );
563
564 if (buf.n > 0) {
565 emit( buf.buf, local_strlen(buf.buf) );
566 }
567
568 va_end(vargs);
569}
570
571
572
573/*--------------------------------------------------------------------*/
574/*--- end m_debuglog.c ---*/
575/*--------------------------------------------------------------------*/