1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2003 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 #include <sys/types.h>
37 #include "vgstrpool.h"
42 #define TIME_STAMP_FORMAT "%u-%.2u-%.2u %.2u:%.2u:%.2u.%.3u"
45 VG_ERROR_PARSER_STATE_INIT
,
46 VG_ERROR_PARSER_STATE_NEW_ERROR
,
47 VG_ERROR_PARSER_STATE_PARTIAL_ERROR
,
48 VG_ERROR_PARSER_STATE_WARNING
= (1 << 8)
51 typedef struct _VgErrorListNode
{
52 struct _VgErrorListNode
*next
;
53 struct _VgErrorListNode
*prev
;
61 VgErrorSummary
*summ_cur
;
62 VgErrorSummary
*summ_tail
;
64 VgErrorStack
*stack_tail
;
69 vg_error_parser_new (int fd
, VgErrorCallback error_cb
, void *user_data
)
71 VgErrorParser
*parser
;
73 parser
= g_new (VgErrorParser
, 1);
74 parser_init ((Parser
*) parser
, fd
);
76 parser
->pid_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
77 list_init (&parser
->errlist
);
79 parser
->error_cb
= error_cb
;
80 parser
->user_data
= user_data
;
87 vg_error_parser_free (VgErrorParser
*parser
)
94 g_hash_table_destroy (parser
->pid_hash
);
96 while (!list_is_empty (&parser
->errlist
)) {
97 n
= (VgErrorListNode
*) list_unlink_head (&parser
->errlist
);
100 vg_error_free (n
->err_cur
);
110 vg_error_get_state (VgErrorParser
*parser
, pid_t pid
)
114 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
))))
115 return VG_ERROR_PARSER_STATE_INIT
;
121 vg_error_set_state (VgErrorParser
*parser
, pid_t pid
, int state
)
125 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
)))) {
126 d(fprintf (stderr
, "VgErrorParser setting state on unknown pid??\n"));
134 vg_error_new (VgErrorParser
*parser
, pid_t pid
, time_stamp_t
*stamp
)
139 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
)))) {
140 n
= g_new (VgErrorListNode
, 1);
143 list_append_node (&parser
->errlist
, (ListNode
*) n
);
144 g_hash_table_insert (parser
->pid_hash
, GINT_TO_POINTER (pid
), n
);
145 } else if (n
->state
== VG_ERROR_PARSER_STATE_NEW_ERROR
) {
149 err
= g_new (VgError
, 1);
150 memcpy (&err
->stamp
, stamp
, sizeof (err
->stamp
));
152 err
->thread
= (vgthread_t
) -1;
157 n
->summ_tail
= (VgErrorSummary
*) &err
->summary
;
158 n
->stack_tail
= NULL
;
159 n
->state
= VG_ERROR_PARSER_STATE_NEW_ERROR
;
165 vg_error_pop (VgErrorParser
*parser
, pid_t pid
)
169 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
))))
173 if (n
->err_cur
->summary
) {
174 parser
->error_cb (parser
, n
->err_cur
, parser
->user_data
);
176 d(fprintf (stderr
, "Incomplete valgrind error being popped??\n"));
180 d(fprintf (stderr
, "VgErrorParser stack underflow??\n"));
183 n
->state
= VG_ERROR_PARSER_STATE_INIT
;
188 n
->stack_tail
= NULL
;
192 vg_error_pop_all (VgErrorParser
*parser
)
196 n
= (VgErrorListNode
*) parser
->errlist
.head
;
197 while (n
->next
!= NULL
) {
198 vg_error_pop (parser
, n
->pid
);
204 vg_error_summary_append (VgErrorParser
*parser
, pid_t pid
, const char *report
, int len
)
206 VgErrorSummary
*summary
;
209 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
)))) {
210 d(fprintf (stderr
, "VgErrorParser appending summary to non-existant error report??\n"));
214 summary
= g_new (VgErrorSummary
, 1);
215 summary
->next
= NULL
;
216 summary
->parent
= n
->err_cur
;
217 summary
->frames
= NULL
;
218 summary
->report
= vg_strndup (report
, len
);
220 n
->summ_cur
= summary
;
221 n
->summ_tail
->next
= summary
;
222 n
->summ_tail
= summary
;
224 n
->stack_tail
= (VgErrorStack
*) &summary
->frames
;
227 static VgErrorStack
*
228 vg_error_stack_new (VgErrorParser
*parser
, pid_t pid
)
233 if (!(n
= g_hash_table_lookup (parser
->pid_hash
, GINT_TO_POINTER (pid
)))) {
234 d(fprintf (stderr
, "VgErrorParser appending stack frame to non-existant error report??\n"));
238 stack
= g_new (VgErrorStack
, 1);
240 stack
->summary
= n
->summ_cur
;
241 stack
->where
= VG_WHERE_AT
;
242 stack
->addr
= VG_STACK_ADDR_UNKNOWN
;
243 stack
->type
= VG_STACK_SOURCE
;
244 stack
->symbol
= NULL
;
245 stack
->info
.src
.filename
= NULL
;
246 stack
->info
.src
.lineno
= 0;
247 stack
->info
.object
= NULL
;
249 n
->stack_tail
->next
= stack
;
250 n
->stack_tail
= stack
;
256 vg_error_parser_step (VgErrorParser
*parser
)
258 register char *inptr
;
268 priv
= (Parser
*) parser
;
270 if ((ret
= parser_fill (priv
)) == 0) {
271 vg_error_pop_all (parser
);
273 } else if (ret
== -1) {
277 start
= inptr
= (char *)priv
->inptr
;
279 while (inptr
< (char *)priv
->inend
) {
281 while (*inptr
!= '\n')
284 d(fprintf (stderr
, "parser_step: '%.*s'\n", inptr
- start
, start
));
286 if (inptr
== (char *)priv
->inend
)
289 if (start
[0] != '=' || start
[1] != '=') {
290 d(fprintf (stderr
, "Unexpected data received from valgrind: '%.*s'\n", inptr
- start
, start
));
299 if ((num
= strtoul (start
, &end
, 10)) == 0 || end
== start
|| *end
!= '=') {
300 /* possible time stamp */
302 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
311 if ((num
= strtoul (start
, &end
, 10)) == 0 || num
> 12 || end
== start
|| *end
!= '-') {
312 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
321 if ((num
= strtoul (start
, &end
, 10)) == 0 || num
> 31 || end
== start
|| *end
!= ' ') {
322 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
331 if ((num
= strtoul (start
, &end
, 10)) > 23 || end
== start
|| *end
!= ':') {
332 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
341 if ((num
= strtoul (start
, &end
, 10)) > 59 || end
== start
|| *end
!= ':') {
342 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
351 if ((num
= strtoul (start
, &end
, 10)) > 59 || end
== start
|| *end
!= '.') {
352 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
361 if ((num
= strtoul (start
, &end
, 10)) > 1000 || end
== start
|| *end
!= ' ') {
362 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
371 if ((pid
= strtoul (start
, &end
, 10)) == 0 || end
== start
|| *end
!= '=') {
372 d(fprintf (stderr
, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end
- start
, start
));
381 if (end
[0] != '=' || end
[1] != '=') {
382 d(fprintf (stderr
, "Unexpected data received from valgrind: '%.*s'\n", (inptr
- start
) + 2, start
- 2));
391 if ((state
= vg_error_get_state (parser
, pid
)) & VG_ERROR_PARSER_STATE_WARNING
) {
392 while (*start
== ' ')
395 if (strcmp (start
, "your program may misbehave as a result") == 0) {
396 /* this marks the end of the Valgrind warning spewage */
397 vg_error_set_state (parser
, pid
, state
& ~VG_ERROR_PARSER_STATE_WARNING
);
405 if (state
!= VG_ERROR_PARSER_STATE_PARTIAL_ERROR
) {
406 /* brand new error - first line is the general report or thread-id (1.9.6 and later) */
407 err
= vg_error_new (parser
, pid
, &stamp
);
409 if (start
[0] == ' ') {
410 d(fprintf (stderr
, "Unexpected SPACE received from valgrind: '%.*s'\n", inptr
- start
, start
));
412 while (*start
== ' ')
417 if (strncmp (start
, "discard syms in ", 16) == 0) {
418 /* "discard syms in /path/to/lib/libfoo.so" */
419 d(fprintf (stderr
, "dropping 'discard syms in' spewage\n"));
420 } else if (strstr (start
, "IGNORED call to:") != NULL
) {
421 d(fprintf (stderr
, "dropping ignored call notification\n"));
422 } else if (strstr (start
, "KLUDGED call to:") != NULL
) {
423 /* "valgrind's libpthread.so: KLUDGED call to: pthread_getschedparam" */
424 d(fprintf (stderr
, "dropping kludged call notification\n"));
425 } else if (strncmp (start
, "warning: ", 9) == 0) {
426 /* "warning: Valgrind's pthread_getschedparam is incomplete" */
427 d(fprintf (stderr
, "dropping warning message\n"));
428 vg_error_set_state (parser
, pid
, state
| VG_ERROR_PARSER_STATE_WARNING
);
429 } else if (strncmp (start
, "Thread ", 7) == 0) {
431 thread
= strtoul (start
, &end
, 10);
434 vg_error_summary_append (parser
, pid
, start
, inptr
- start
);
435 vg_error_set_state (parser
, pid
, VG_ERROR_PARSER_STATE_PARTIAL_ERROR
);
437 err
->thread
= thread
;
440 vg_error_summary_append (parser
, pid
, start
, inptr
- start
);
441 vg_error_set_state (parser
, pid
, VG_ERROR_PARSER_STATE_PARTIAL_ERROR
);
445 /* another summary, a new stack frame, or end-of-info (ie. a blank line) */
446 while (*start
== ' ')
450 if (!strncmp (start
, "discard syms in ", 16)) {
451 /* "discard syms in /path/to/lib/libfoo.so" */
452 d(fprintf (stderr
, "dropping 'discard syms in' spew received from Valgrind\n"));
453 } else if (strstr (start
, "IGNORED call to:") != NULL
) {
454 d(fprintf (stderr
, "dropping ignored call notification\n"));
455 } else if (strstr (start
, "KLUDGED call to:") != NULL
) {
456 /* "valgrind's libpthread.so: KLUDGED call to: pthread_getschedparam" */
457 d(fprintf (stderr
, "dropping kludged call notification\n"));
458 } else if (strncmp (start
, "warning: ", 9) == 0) {
459 /* "warning: Valgrind's pthread_getschedparam is incomplete" */
460 d(fprintf (stderr
, "dropping warning message\n"));
461 vg_error_set_state (parser
, pid
, state
| VG_ERROR_PARSER_STATE_WARNING
);
462 } else if (strncmp (start
, "at ", 3) != 0 && strncmp (start
, "by ", 3) != 0) {
463 /* another summary report */
464 vg_error_summary_append (parser
, pid
, start
, inptr
- start
);
468 stack
= vg_error_stack_new (parser
, pid
);
469 stack
->where
= *start
== 'a' ? VG_WHERE_AT
: VG_WHERE_BY
;
473 /* unknown address */
474 stack
->addr
= VG_STACK_ADDR_UNKNOWN
;
476 while (start
< inptr
&& *start
!= '>')
482 /* symbol address in hex */
483 stack
->addr
= strtoul (start
, (char **) &end
, 16);
493 if (strncmp (start
, "??? ", 4) == 0) {
494 stack
->symbol
= NULL
;
496 } else if (*start
== '(') {
497 /* not a c/c++ program, hence no symbol */
498 stack
->symbol
= NULL
;
503 while (end
< inptr
&& *end
!= ' ' && *end
!= '(')
507 /* symbol name has a param list - probably a c++ symbol */
508 while (end
< inptr
&& *end
!= ')')
515 stack
->symbol
= vg_strndup (start
, end
- start
);
525 /* if we have "([with]in foo)" then foo is an object... */
526 if (strncmp (start
, "within ", 7) == 0) {
527 /* (within /usr/bin/emacs) */
528 stack
->type
= VG_STACK_OBJECT
;
530 } else if (strncmp (start
, "in ", 3) == 0) {
531 /* (in /lib/foo.so) */
532 stack
->type
= VG_STACK_OBJECT
;
535 stack
->type
= VG_STACK_SOURCE
;
539 while (end
< inptr
&& *end
!= ':' && *end
!= ')')
542 /* src filename or shared object */
543 if (stack
->type
== VG_STACK_SOURCE
) {
544 stack
->info
.src
.filename
= vg_strndup (start
, end
- start
);
548 stack
->info
.src
.lineno
= strtoul (start
, (char **) &end
, 10);
550 stack
->info
.src
.lineno
= 0;
552 stack
->info
.object
= vg_strndup (start
, end
- start
);
559 /* end-of-info (ie. a blank line) */
560 vg_error_pop (parser
, pid
);
568 priv
->inptr
= (unsigned char *)start
;
575 vg_error_parser_flush (VgErrorParser
*parser
)
579 n
= (VgErrorListNode
*) parser
->errlist
.head
;
580 while (n
->next
!= NULL
) {
582 if (n
->state
== VG_ERROR_PARSER_STATE_PARTIAL_ERROR
) {
583 vg_error_pop (parser
, n
->pid
);
596 vg_error_stack_free (VgErrorStack
*stack
)
598 vg_strfree (stack
->symbol
);
599 if (stack
->type
== VG_STACK_SOURCE
)
600 vg_strfree (stack
->info
.src
.filename
);
602 vg_strfree (stack
->info
.object
);
608 vg_error_summary_free (VgErrorSummary
*summary
)
610 VgErrorStack
*frame
, *next
;
612 vg_strfree (summary
->report
);
614 frame
= summary
->frames
;
615 while (frame
!= NULL
) {
617 vg_error_stack_free (frame
);
626 vg_error_free (VgError
*err
)
628 VgErrorSummary
*summary
, *next
;
633 summary
= err
->summary
;
634 while (summary
!= NULL
) {
635 next
= summary
->next
;
636 vg_error_summary_free (summary
);
645 vg_error_stack_to_string (VgErrorStack
*stack
, GString
*str
)
647 time_stamp_t
*stamp
= &stack
->summary
->parent
->stamp
;
648 pid_t pid
= stack
->summary
->parent
->pid
;
650 g_string_append (str
, "==");
652 if (stamp
->year
!= 0) {
653 g_string_append_printf (str
, TIME_STAMP_FORMAT
" ", stamp
->year
,
654 stamp
->month
, stamp
->day
, stamp
->hour
,
655 stamp
->min
, stamp
->sec
, stamp
->msec
);
658 g_string_append_printf (str
, "%u== %s ", pid
, stack
->where
== VG_WHERE_AT
? "at" : "by");
660 if (stack
->addr
!= VG_STACK_ADDR_UNKNOWN
)
661 g_string_append_printf (str
, "0x%.8x: ", stack
->addr
);
663 g_string_append (str
, "<unknown address> ");
665 g_string_append (str
, stack
->symbol
? stack
->symbol
: "???");
667 if (stack
->type
== VG_STACK_SOURCE
) {
668 g_string_append_printf (str
, " (%s:%u)\n", stack
->info
.src
.filename
, stack
->info
.src
.lineno
);
672 in
= !strcmp (stack
->info
.object
+ strlen (stack
->info
.object
) - 3, ".so");
673 in
= in
|| strstr (stack
->info
.object
, ".so.") != NULL
;
674 g_string_append_printf (str
, " (%s %s)\n", in
? "in" : "within", stack
->info
.object
);
679 vg_error_summary_to_string (VgErrorSummary
*summary
, int indent
, GString
*str
)
681 time_stamp_t
*stamp
= &summary
->parent
->stamp
;
684 g_string_append (str
, "==");
686 if (stamp
->year
!= 0) {
687 g_string_append_printf (str
, TIME_STAMP_FORMAT
" ", stamp
->year
,
688 stamp
->month
, stamp
->day
, stamp
->hour
,
689 stamp
->min
, stamp
->sec
, stamp
->msec
);
692 g_string_append_printf (str
, "%u== %s", summary
->parent
->pid
, indent
? " " : "");
693 g_string_append (str
, summary
->report
);
694 g_string_append_c (str
, '\n');
698 vg_error_stack_to_string (s
, str
);
705 vg_error_to_string (VgError
*err
, GString
*str
)
710 if (err
->thread
!= (vgthread_t
) -1) {
711 g_string_append (str
, "==");
712 if (err
->stamp
.year
!= 0) {
713 g_string_append_printf (str
, TIME_STAMP_FORMAT
" ", err
->stamp
.year
,
714 err
->stamp
.month
, err
->stamp
.day
, err
->stamp
.hour
,
715 err
->stamp
.min
, err
->stamp
.sec
, err
->stamp
.msec
);
717 g_string_append_printf (str
, "%u== Thread %ld:\n", err
->pid
, err
->thread
);
722 vg_error_summary_to_string (s
, indent
, str
);
723 indent
= indent
|| s
->frames
;
727 g_string_append (str
, "==");
728 if (err
->stamp
.year
!= 0) {
729 g_string_append_printf (str
, TIME_STAMP_FORMAT
" ", err
->stamp
.year
,
730 err
->stamp
.month
, err
->stamp
.day
, err
->stamp
.hour
,
731 err
->stamp
.min
, err
->stamp
.sec
, err
->stamp
.msec
);
733 g_string_append_printf (str
, "%u==\n", err
->pid
);