blob: 48d5caf95927ad0ed0a913083c079c12bb082af7 [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 */
sewardj45f4e7c2005-09-27 19:20:21 +000051#include "valgrind.h" /* for RUNNING_ON_VALGRIND */
sewardj1cf558c2005-04-25 01:36:56 +000052
53/*------------------------------------------------------------*/
54/*--- Stuff to make us completely independent. ---*/
55/*------------------------------------------------------------*/
56
57/* ----- x86-linux specifics ----- */
58
sewardj21c6d0f2005-05-02 10:33:44 +000059#if defined(VGP_x86_linux)
sewardj1cf558c2005-04-25 01:36:56 +000060
61static UInt local_sys_write_stderr ( HChar* buf, Int n )
62{
63 UInt __res;
tom311400b2005-04-27 08:58:53 +000064 __asm__ volatile (
sewardj45f4e7c2005-09-27 19:20:21 +000065 "pushl %%ebx\n" // ebx is callee-save
sewardjd485d692005-04-28 09:40:53 +000066 "movl $4, %%eax\n" /* %eax = __NR_write */
sewardj45f4e7c2005-09-27 19:20:21 +000067 "movl $1, %%ebx\n" /* %ebx = stderr */
sewardjd485d692005-04-28 09:40:53 +000068 "movl %1, %%ecx\n" /* %ecx = buf */
69 "movl %2, %%edx\n" /* %edx = n */
sewardjd485d692005-04-28 09:40:53 +000070 "int $0x80\n" /* write(stderr, buf, n) */
sewardjd485d692005-04-28 09:40:53 +000071 "movl %%eax, %0\n" /* __res = eax */
sewardj45f4e7c2005-09-27 19:20:21 +000072 "popl %%ebx\n" // restore ebx
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
sewardjdaf77af2005-07-19 14:17:37 +0000264/* Copy a string into the buffer, escaping bad XML chars. */
265static
266UInt myvprintf_str_XML_simplistic ( void(*send)(HChar,void*),
267 void* send_arg2,
268 HChar* str )
269{
270 UInt ret = 0;
271 Int i;
272 Int len = local_strlen(str);
273 HChar* alt;
274
275 for (i = 0; i < len; i++) {
276 switch (str[i]) {
277 case '&': alt = "&amp;"; break;
278 case '<': alt = "&lt;"; break;
279 case '>': alt = "&gt;"; break;
280 default: alt = NULL;
281 }
282
283 if (alt) {
284 while (*alt) {
285 send(*alt, send_arg2);
286 ret++;
287 alt++;
288 }
289 } else {
290 send(str[i], send_arg2);
291 ret++;
292 }
293 }
294
295 return ret;
296}
297
298
sewardj1cf558c2005-04-25 01:36:56 +0000299/* Write P into the buffer according to these args:
300 * If SIGN is true, p is a signed.
301 * BASE is the base.
302 * If WITH_ZERO is true, '0' must be added.
303 * WIDTH is the width of the field.
304 */
305static
306UInt myvprintf_int64 ( void(*send)(HChar,void*),
307 void* send_arg2,
308 Int flags,
309 Int base,
310 Int width,
311 ULong p )
312{
313 HChar buf[40];
314 Int ind = 0;
315 Int i, nc = 0;
316 Bool neg = False;
317 HChar* digits = "0123456789ABCDEF";
318 UInt ret = 0;
319
320 if (base < 2 || base > 16)
321 return ret;
322
323 if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
324 p = - (Long)p;
325 neg = True;
326 }
327
328 if (p == 0)
329 buf[ind++] = '0';
330 else {
331 while (p > 0) {
332 if (flags & VG_MSG_COMMA && 10 == base &&
333 0 == (ind-nc) % 3 && 0 != ind)
334 {
335 buf[ind++] = ',';
336 nc++;
337 }
338 buf[ind++] = digits[p % base];
339 p /= base;
340 }
341 }
342
343 if (neg)
344 buf[ind++] = '-';
345
346 if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
347 for(; ind < width; ind++) {
348 /* vg_assert(ind < 39); */
349 if (ind > 39) {
350 buf[39] = 0;
351 break;
352 }
353 buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
354 }
355 }
356
357 /* Reverse copy to buffer. */
358 ret += ind;
359 for (i = ind -1; i >= 0; i--) {
360 send(buf[i], send_arg2);
361 }
362 if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
363 for(; ind < width; ind++) {
364 ret++;
365 /* Never pad with zeroes on RHS -- changes the value! */
366 send(' ', send_arg2);
367 }
368 }
369 return ret;
370}
371
372
373/* A simple vprintf(). */
374/* EXPORTED */
375UInt
376VG_(debugLog_vprintf) (
377 void(*send)(HChar,void*),
378 void* send_arg2,
379 const HChar* format,
380 va_list vargs
381)
382{
383 UInt ret = 0;
384 Int i;
385 Int flags;
386 Int width;
njn68e46592005-08-26 19:42:27 +0000387 Int n_ls = 0;
sewardj1cf558c2005-04-25 01:36:56 +0000388 Bool is_long;
389
390 /* We assume that vargs has already been initialised by the
391 caller, using va_start, and that the caller will similarly
392 clean up with va_end.
393 */
394
395 for (i = 0; format[i] != 0; i++) {
396 if (format[i] != '%') {
397 send(format[i], send_arg2);
398 ret++;
399 continue;
400 }
401 i++;
402 /* A '%' has been found. Ignore a trailing %. */
403 if (format[i] == 0)
404 break;
405 if (format[i] == '%') {
njn02bc4b82005-05-15 17:28:26 +0000406 /* '%%' is replaced by '%'. */
sewardj1cf558c2005-04-25 01:36:56 +0000407 send('%', send_arg2);
408 ret++;
409 continue;
410 }
411 flags = 0;
njn68e46592005-08-26 19:42:27 +0000412 n_ls = 0;
sewardj1cf558c2005-04-25 01:36:56 +0000413 width = 0; /* length of the field. */
414 if (format[i] == '(') {
415 flags |= VG_MSG_PAREN;
416 i++;
417 }
418 /* If ',' follows '%', commas will be inserted. */
419 if (format[i] == ',') {
420 flags |= VG_MSG_COMMA;
421 i++;
422 }
423 /* If '-' follows '%', justify on the left. */
424 if (format[i] == '-') {
425 flags |= VG_MSG_LJUSTIFY;
426 i++;
427 }
428 /* If '0' follows '%', pads will be inserted. */
429 if (format[i] == '0') {
430 flags |= VG_MSG_ZJUSTIFY;
431 i++;
432 }
433 /* Compute the field length. */
434 while (format[i] >= '0' && format[i] <= '9') {
435 width *= 10;
436 width += format[i++] - '0';
437 }
438 while (format[i] == 'l') {
439 i++;
njn68e46592005-08-26 19:42:27 +0000440 n_ls++;
sewardj1cf558c2005-04-25 01:36:56 +0000441 }
442
njn68e46592005-08-26 19:42:27 +0000443 // %d means print a 32-bit integer.
444 // %ld means print a word-size integer.
445 // %lld means print a 64-bit integer.
446 if (0 == n_ls) { is_long = False; }
447 else if (1 == n_ls) { is_long = ( sizeof(void*) == sizeof(Long) ); }
448 else { is_long = True; }
449
sewardj1cf558c2005-04-25 01:36:56 +0000450 switch (format[i]) {
451 case 'd': /* %d */
452 flags |= VG_MSG_SIGNED;
453 if (is_long)
454 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
455 (ULong)(va_arg (vargs, Long)));
456 else
457 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
458 (ULong)(va_arg (vargs, Int)));
459 break;
460 case 'u': /* %u */
461 if (is_long)
462 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
463 (ULong)(va_arg (vargs, ULong)));
464 else
465 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
466 (ULong)(va_arg (vargs, UInt)));
467 break;
468 case 'p': /* %p */
469 ret += 2;
470 send('0',send_arg2);
471 send('x',send_arg2);
472 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
473 (ULong)((UWord)va_arg (vargs, void *)));
474 break;
475 case 'x': /* %x */
476 if (is_long)
477 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
478 (ULong)(va_arg (vargs, ULong)));
479 else
480 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
481 (ULong)(va_arg (vargs, UInt)));
482 break;
483 case 'c': /* %c */
484 ret++;
485 send(va_arg (vargs, int), send_arg2);
486 break;
487 case 's': case 'S': { /* %s */
488 char *str = va_arg (vargs, char *);
489 if (str == (char*) 0) str = "(null)";
490 ret += myvprintf_str(send, send_arg2,
491 flags, width, str, format[i]=='S');
492 break;
493 }
sewardjdaf77af2005-07-19 14:17:37 +0000494 case 't': { /* %t, like %s but escaping chars for XML safety */
495 /* Note: simplistic; ignores field width and flags */
496 char *str = va_arg (vargs, char *);
497 if (str == (char*) 0) str = "(null)";
498 ret += myvprintf_str_XML_simplistic(send, send_arg2, str);
499 break;
500 }
501
sewardj1cf558c2005-04-25 01:36:56 +0000502// case 'y': { /* %y - print symbol */
503// Char buf[100];
504// Char *cp = buf;
505// Addr a = va_arg(vargs, Addr);
506//
507// if (flags & VG_MSG_PAREN)
508// *cp++ = '(';
509// if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) {
510// if (flags & VG_MSG_PAREN) {
511// cp += VG_(strlen)(cp);
512// *cp++ = ')';
513// *cp = '\0';
514// }
515// ret += myvprintf_str(send, send_arg2, flags, width, buf, 0);
516// }
517// break;
518// }
519 default:
520 break;
521 }
522 }
523 return ret;
524}
525
526
527/*------------------------------------------------------------*/
528/*--- Debuglog stuff. ---*/
529/*------------------------------------------------------------*/
530
531/* Only print messages whose stated level is less than or equal to
532 this. By default, it makes this entire subsystem silent. */
533
534static Int loglevel = 0;
535
sewardj1cf558c2005-04-25 01:36:56 +0000536/* Module startup. */
sewardj10759312005-05-30 23:52:47 +0000537/* EXPORTED */
sewardj1cf558c2005-04-25 01:36:56 +0000538void VG_(debugLog_startup) ( Int level, HChar* who )
539{
540 if (level < 0) level = 0;
541 if (level > 10) level = 10;
542 loglevel = level;
543 VG_(debugLog)(1, "debuglog",
544 "DebugLog system started by %s, "
545 "level %d logging requested\n",
546 who, loglevel);
547}
548
sewardj10759312005-05-30 23:52:47 +0000549/* Get the logging threshold level, as set by the most recent call to
550 VG_(debugLog_startup), or zero if there have been no such calls so
551 far. */
552/* EXPORTED */
553Int VG_(debugLog_getLevel) ( void )
554{
555 return loglevel;
556}
557
558
sewardj1cf558c2005-04-25 01:36:56 +0000559/* ------------ */
560
561typedef
562 struct {
563 HChar buf[100];
564 Int n;
565 }
566 printf_buf;
567
568static void add_to_buf ( HChar c, void* p )
569{
570 printf_buf* buf = (printf_buf*)p;
571
572 if (buf->n >= 100-10 /*paranoia*/ ) {
573 emit( buf->buf, local_strlen(buf->buf) );
574 buf->n = 0;
575 buf->buf[buf->n] = 0;
576 }
577 buf->buf[buf->n++] = c;
578 buf->buf[buf->n] = 0;
579}
580
581/* Send a logging message. Nothing is output unless 'level'
582 is <= the current loglevel. */
583/* EXPORTED */
584__attribute__((format(__printf__, 3, 4)))
585void VG_(debugLog) ( Int level, const HChar* modulename,
586 const HChar* format, ... )
587{
588 UInt ret, pid;
sewardj45f4e7c2005-09-27 19:20:21 +0000589 Int indent, depth, i;
sewardj1cf558c2005-04-25 01:36:56 +0000590 va_list vargs;
591 printf_buf buf;
sewardja5ebfa92005-04-25 02:04:54 +0000592
sewardj1cf558c2005-04-25 01:36:56 +0000593 if (level > loglevel)
594 return;
595
sewardjd85feff2005-04-25 02:37:56 +0000596 indent = 2*level - 1;
sewardja5ebfa92005-04-25 02:04:54 +0000597 if (indent < 1) indent = 1;
598
sewardj1cf558c2005-04-25 01:36:56 +0000599 buf.n = 0;
600 buf.buf[0] = 0;
601 pid = local_sys_getpid();
sewardj45f4e7c2005-09-27 19:20:21 +0000602
603 // Print one '>' in front of the messages for each level of self-hosting
604 // being performed.
605 depth = RUNNING_ON_VALGRIND;
606 for (i = 0; i < depth; i++) {
607 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ">", False );
608 }
609
sewardja5ebfa92005-04-25 02:04:54 +0000610 (void)myvprintf_str ( add_to_buf, &buf, 0, 2, "--", False );
611 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)pid );
612 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000613 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)level );
sewardja5ebfa92005-04-25 02:04:54 +0000614 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000615 (void)myvprintf_str ( add_to_buf, &buf, 0, 8, (HChar*)modulename, False );
sewardja5ebfa92005-04-25 02:04:54 +0000616 (void)myvprintf_str ( add_to_buf, &buf, 0, indent, "", False );
sewardj1cf558c2005-04-25 01:36:56 +0000617
618 va_start(vargs,format);
619
620 ret = VG_(debugLog_vprintf) ( add_to_buf, &buf, format, vargs );
621
622 if (buf.n > 0) {
623 emit( buf.buf, local_strlen(buf.buf) );
624 }
625
626 va_end(vargs);
627}
628
629
630
631/*--------------------------------------------------------------------*/
632/*--- end m_debuglog.c ---*/
633/*--------------------------------------------------------------------*/