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