blob: 940d1881e017b4f63c01cd20ab7ed4f89c104bbc [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
sewardj601371a2005-04-25 16:21:17 +000058/* Arse! Really I want to test VG_PLATFORM, but this does not
59 seem to be possible. */
60#if defined(__i386__) && defined(__linux__)
sewardj1cf558c2005-04-25 01:36:56 +000061
62static UInt local_sys_write_stderr ( HChar* buf, Int n )
63{
64 UInt __res;
tom311400b2005-04-27 08:58:53 +000065 __asm__ volatile (
sewardjd485d692005-04-28 09:40:53 +000066 "movl $4, %%eax\n" /* %eax = __NR_write */
67 "movl $2, %%edi\n" /* %edi = stderr */
68 "movl %1, %%ecx\n" /* %ecx = buf */
69 "movl %2, %%edx\n" /* %edx = n */
70 "pushl %%ebx\n"
71 "movl %%edi, %%ebx\n"
72 "int $0x80\n" /* write(stderr, buf, n) */
73 "popl %%ebx\n"
74 "movl %%eax, %0\n" /* __res = eax */
tom311400b2005-04-27 08:58:53 +000075 : "=mr" (__res)
sewardjd4d203b2005-04-27 23:17:48 +000076 : "g" (buf), "g" (n)
sewardjd485d692005-04-28 09:40:53 +000077 : "eax", "edi", "ecx", "edx"
sewardjd4d203b2005-04-27 23:17:48 +000078 );
sewardj1cf558c2005-04-25 01:36:56 +000079 if (__res < 0)
80 __res = -1;
81 return __res;
82}
83
84static UInt local_sys_getpid ( void )
85{
86 UInt __res;
tom311400b2005-04-27 08:58:53 +000087 __asm__ volatile (
88 "movl $20, %%eax\n" /* set %eax = __NR_getpid */
89 "int $0x80\n" /* getpid() */
90 "movl %%eax, %0\n" /* set __res = eax */
sewardjd4d203b2005-04-27 23:17:48 +000091 : "=mr" (__res)
92 :
93 : "eax" );
sewardj1cf558c2005-04-25 01:36:56 +000094 return __res;
95}
96
sewardj601371a2005-04-25 16:21:17 +000097#elif defined(__x86_64__) && defined(__linux__)
98
99static UInt local_sys_write_stderr ( HChar* buf, Int n )
100{
tomc6121862005-04-27 09:23:02 +0000101 UInt __res;
102 __asm__ volatile (
103 "movq $1, %%rax\n" /* set %rax = __NR_write */
104 "movq $2, %%rdi\n" /* set %rdi = stderr */
105 "movq %1, %%rsi\n" /* set %rsi = buf */
sewardjd4d203b2005-04-27 23:17:48 +0000106 "movl %2, %%edx\n" /* set %edx = n */
tomc6121862005-04-27 09:23:02 +0000107 "syscall\n" /* write(stderr, buf, n) */
108 "movl %%eax, %0\n" /* set __res = %eax */
109 : "=mr" (__res)
110 : "g" (buf), "g" (n)
111 : "rax", "rdi", "rsi", "rdx" );
112 if (__res < 0)
113 __res = -1;
114 return __res;
sewardj601371a2005-04-25 16:21:17 +0000115}
116
117static UInt local_sys_getpid ( void )
118{
tomc6121862005-04-27 09:23:02 +0000119 UInt __res;
120 __asm__ volatile (
121 "movq $39, %%rax\n" /* set %rax = __NR_getpid */
122 "syscall\n" /* getpid() */
123 "movl %%eax, %0\n" /* set __res = %eax */
124 : "=mr" (__res)
125 :
126 : "rax" );
127 return __res;
sewardj601371a2005-04-25 16:21:17 +0000128}
sewardj1cf558c2005-04-25 01:36:56 +0000129
130#else
131#error Unknown VG_PLATFORM
132#endif
133
134
135/* ----- generic ----- */
136
137/* strlen, so we don't need m_libc */
138static Int local_strlen ( const HChar* str )
139{
140 Int i = 0;
141 while (str[i] != 0) i++;
142 return i;
143}
144
145static HChar local_toupper ( HChar c )
146{
147 if (c >= 'a' && c <= 'z')
148 return c + ('A' - 'a');
149 else
150 return c;
151}
152
153/* Emit buf[0 .. n-1] to stderr. Unfortunately platform-specific.
154*/
155static void emit ( HChar* buf, Int n )
156{
157 if (n >= 1)
158 (void)local_sys_write_stderr(buf, n);
159}
160
161
162/*------------------------------------------------------------*/
163/*--- A simple, generic, vprintf implementation. ---*/
164/*------------------------------------------------------------*/
165
166/* -----------------------------------------------
167 Distantly derived from:
168
169 vprintf replacement for Checker.
170 Copyright 1993, 1994, 1995 Tristan Gingold
171 Written September 1993 Tristan Gingold
172 Tristan Gingold, 8 rue Parmentier, F-91120 PALAISEAU, FRANCE
173
174 (Checker itself was GPL'd.)
175 ----------------------------------------------- */
176
177/* Some flags. */
178#define VG_MSG_SIGNED 1 /* The value is signed. */
179#define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */
180#define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */
181#define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */
182#define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */
183
184
185/* Copy a string into the buffer. */
186static
187UInt myvprintf_str ( void(*send)(HChar,void*),
188 void* send_arg2,
189 Int flags,
190 Int width,
191 HChar* str,
192 Bool capitalise )
193{
194# define MAYBE_TOUPPER(ch) (capitalise ? local_toupper(ch) : (ch))
195 UInt ret = 0;
196 Int i, extra;
197 Int len = local_strlen(str);
198
199 if (width == 0) {
200 ret += len;
201 for (i = 0; i < len; i++)
202 send(MAYBE_TOUPPER(str[i]), send_arg2);
203 return ret;
204 }
205
206 if (len > width) {
207 ret += width;
208 for (i = 0; i < width; i++)
209 send(MAYBE_TOUPPER(str[i]), send_arg2);
210 return ret;
211 }
212
213 extra = width - len;
214 if (flags & VG_MSG_LJUSTIFY) {
215 ret += extra;
216 for (i = 0; i < extra; i++)
217 send(' ', send_arg2);
218 }
219 ret += len;
220 for (i = 0; i < len; i++)
221 send(MAYBE_TOUPPER(str[i]), send_arg2);
222 if (!(flags & VG_MSG_LJUSTIFY)) {
223 ret += extra;
224 for (i = 0; i < extra; i++)
225 send(' ', send_arg2);
226 }
227
228# undef MAYBE_TOUPPER
229 return ret;
230}
231
232
233/* Write P into the buffer according to these args:
234 * If SIGN is true, p is a signed.
235 * BASE is the base.
236 * If WITH_ZERO is true, '0' must be added.
237 * WIDTH is the width of the field.
238 */
239static
240UInt myvprintf_int64 ( void(*send)(HChar,void*),
241 void* send_arg2,
242 Int flags,
243 Int base,
244 Int width,
245 ULong p )
246{
247 HChar buf[40];
248 Int ind = 0;
249 Int i, nc = 0;
250 Bool neg = False;
251 HChar* digits = "0123456789ABCDEF";
252 UInt ret = 0;
253
254 if (base < 2 || base > 16)
255 return ret;
256
257 if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
258 p = - (Long)p;
259 neg = True;
260 }
261
262 if (p == 0)
263 buf[ind++] = '0';
264 else {
265 while (p > 0) {
266 if (flags & VG_MSG_COMMA && 10 == base &&
267 0 == (ind-nc) % 3 && 0 != ind)
268 {
269 buf[ind++] = ',';
270 nc++;
271 }
272 buf[ind++] = digits[p % base];
273 p /= base;
274 }
275 }
276
277 if (neg)
278 buf[ind++] = '-';
279
280 if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
281 for(; ind < width; ind++) {
282 /* vg_assert(ind < 39); */
283 if (ind > 39) {
284 buf[39] = 0;
285 break;
286 }
287 buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
288 }
289 }
290
291 /* Reverse copy to buffer. */
292 ret += ind;
293 for (i = ind -1; i >= 0; i--) {
294 send(buf[i], send_arg2);
295 }
296 if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
297 for(; ind < width; ind++) {
298 ret++;
299 /* Never pad with zeroes on RHS -- changes the value! */
300 send(' ', send_arg2);
301 }
302 }
303 return ret;
304}
305
306
307/* A simple vprintf(). */
308/* EXPORTED */
309UInt
310VG_(debugLog_vprintf) (
311 void(*send)(HChar,void*),
312 void* send_arg2,
313 const HChar* format,
314 va_list vargs
315)
316{
317 UInt ret = 0;
318 Int i;
319 Int flags;
320 Int width;
321 Bool is_long;
322
323 /* We assume that vargs has already been initialised by the
324 caller, using va_start, and that the caller will similarly
325 clean up with va_end.
326 */
327
328 for (i = 0; format[i] != 0; i++) {
329 if (format[i] != '%') {
330 send(format[i], send_arg2);
331 ret++;
332 continue;
333 }
334 i++;
335 /* A '%' has been found. Ignore a trailing %. */
336 if (format[i] == 0)
337 break;
338 if (format[i] == '%') {
339 /* `%%' is replaced by `%'. */
340 send('%', send_arg2);
341 ret++;
342 continue;
343 }
344 flags = 0;
345 is_long = False;
346 width = 0; /* length of the field. */
347 if (format[i] == '(') {
348 flags |= VG_MSG_PAREN;
349 i++;
350 }
351 /* If ',' follows '%', commas will be inserted. */
352 if (format[i] == ',') {
353 flags |= VG_MSG_COMMA;
354 i++;
355 }
356 /* If '-' follows '%', justify on the left. */
357 if (format[i] == '-') {
358 flags |= VG_MSG_LJUSTIFY;
359 i++;
360 }
361 /* If '0' follows '%', pads will be inserted. */
362 if (format[i] == '0') {
363 flags |= VG_MSG_ZJUSTIFY;
364 i++;
365 }
366 /* Compute the field length. */
367 while (format[i] >= '0' && format[i] <= '9') {
368 width *= 10;
369 width += format[i++] - '0';
370 }
371 while (format[i] == 'l') {
372 i++;
373 is_long = True;
374 }
375
376 switch (format[i]) {
377 case 'd': /* %d */
378 flags |= VG_MSG_SIGNED;
379 if (is_long)
380 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
381 (ULong)(va_arg (vargs, Long)));
382 else
383 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
384 (ULong)(va_arg (vargs, Int)));
385 break;
386 case 'u': /* %u */
387 if (is_long)
388 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
389 (ULong)(va_arg (vargs, ULong)));
390 else
391 ret += myvprintf_int64(send, send_arg2, flags, 10, width,
392 (ULong)(va_arg (vargs, UInt)));
393 break;
394 case 'p': /* %p */
395 ret += 2;
396 send('0',send_arg2);
397 send('x',send_arg2);
398 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
399 (ULong)((UWord)va_arg (vargs, void *)));
400 break;
401 case 'x': /* %x */
402 if (is_long)
403 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
404 (ULong)(va_arg (vargs, ULong)));
405 else
406 ret += myvprintf_int64(send, send_arg2, flags, 16, width,
407 (ULong)(va_arg (vargs, UInt)));
408 break;
409 case 'c': /* %c */
410 ret++;
411 send(va_arg (vargs, int), send_arg2);
412 break;
413 case 's': case 'S': { /* %s */
414 char *str = va_arg (vargs, char *);
415 if (str == (char*) 0) str = "(null)";
416 ret += myvprintf_str(send, send_arg2,
417 flags, width, str, format[i]=='S');
418 break;
419 }
420// case 'y': { /* %y - print symbol */
421// Char buf[100];
422// Char *cp = buf;
423// Addr a = va_arg(vargs, Addr);
424//
425// if (flags & VG_MSG_PAREN)
426// *cp++ = '(';
427// if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) {
428// if (flags & VG_MSG_PAREN) {
429// cp += VG_(strlen)(cp);
430// *cp++ = ')';
431// *cp = '\0';
432// }
433// ret += myvprintf_str(send, send_arg2, flags, width, buf, 0);
434// }
435// break;
436// }
437 default:
438 break;
439 }
440 }
441 return ret;
442}
443
444
445/*------------------------------------------------------------*/
446/*--- Debuglog stuff. ---*/
447/*------------------------------------------------------------*/
448
449/* Only print messages whose stated level is less than or equal to
450 this. By default, it makes this entire subsystem silent. */
451
452static Int loglevel = 0;
453
454/* EXPORTED */
455/* Module startup. */
456void VG_(debugLog_startup) ( Int level, HChar* who )
457{
458 if (level < 0) level = 0;
459 if (level > 10) level = 10;
460 loglevel = level;
461 VG_(debugLog)(1, "debuglog",
462 "DebugLog system started by %s, "
463 "level %d logging requested\n",
464 who, loglevel);
465}
466
467/* ------------ */
468
469typedef
470 struct {
471 HChar buf[100];
472 Int n;
473 }
474 printf_buf;
475
476static void add_to_buf ( HChar c, void* p )
477{
478 printf_buf* buf = (printf_buf*)p;
479
480 if (buf->n >= 100-10 /*paranoia*/ ) {
481 emit( buf->buf, local_strlen(buf->buf) );
482 buf->n = 0;
483 buf->buf[buf->n] = 0;
484 }
485 buf->buf[buf->n++] = c;
486 buf->buf[buf->n] = 0;
487}
488
489/* Send a logging message. Nothing is output unless 'level'
490 is <= the current loglevel. */
491/* EXPORTED */
492__attribute__((format(__printf__, 3, 4)))
493void VG_(debugLog) ( Int level, const HChar* modulename,
494 const HChar* format, ... )
495{
496 UInt ret, pid;
sewardja5ebfa92005-04-25 02:04:54 +0000497 Int indent;
sewardj1cf558c2005-04-25 01:36:56 +0000498 va_list vargs;
499 printf_buf buf;
sewardja5ebfa92005-04-25 02:04:54 +0000500
sewardj1cf558c2005-04-25 01:36:56 +0000501
502 if (level > loglevel)
503 return;
504
sewardjd85feff2005-04-25 02:37:56 +0000505 indent = 2*level - 1;
sewardja5ebfa92005-04-25 02:04:54 +0000506 if (indent < 1) indent = 1;
507
sewardj1cf558c2005-04-25 01:36:56 +0000508 buf.n = 0;
509 buf.buf[0] = 0;
510 pid = local_sys_getpid();
sewardja5ebfa92005-04-25 02:04:54 +0000511 (void)myvprintf_str ( add_to_buf, &buf, 0, 2, "--", False );
512 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)pid );
513 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000514 (void)myvprintf_int64 ( add_to_buf, &buf, 0, 10, 1, (ULong)level );
sewardja5ebfa92005-04-25 02:04:54 +0000515 (void)myvprintf_str ( add_to_buf, &buf, 0, 1, ":", False );
sewardj1cf558c2005-04-25 01:36:56 +0000516 (void)myvprintf_str ( add_to_buf, &buf, 0, 8, (HChar*)modulename, False );
sewardja5ebfa92005-04-25 02:04:54 +0000517 (void)myvprintf_str ( add_to_buf, &buf, 0, indent, "", False );
sewardj1cf558c2005-04-25 01:36:56 +0000518
519 va_start(vargs,format);
520
521 ret = VG_(debugLog_vprintf) ( add_to_buf, &buf, format, vargs );
522
523 if (buf.n > 0) {
524 emit( buf.buf, local_strlen(buf.buf) );
525 }
526
527 va_end(vargs);
528}
529
530
531
532/*--------------------------------------------------------------------*/
533/*--- end m_debuglog.c ---*/
534/*--------------------------------------------------------------------*/