Updated Spanish translation
[anjuta-git-plugin.git] / plugins / valgrind / vgerror.c
blobd3f37f1068186dde95ee5c16b999178842d94dc7
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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.
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <glib.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <errno.h>
36 #include "vgerror.h"
37 #include "vgstrpool.h"
40 #define d(x)
42 #define TIME_STAMP_FORMAT "%u-%.2u-%.2u %.2u:%.2u:%.2u.%.3u"
44 enum {
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;
55 int state;
57 pid_t pid;
59 VgError *err_cur;
61 VgErrorSummary *summ_cur;
62 VgErrorSummary *summ_tail;
64 VgErrorStack *stack_tail;
65 } VgErrorListNode;
68 VgErrorParser *
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;
82 return parser;
86 void
87 vg_error_parser_free (VgErrorParser *parser)
89 VgErrorListNode *n;
91 if (parser == NULL)
92 return;
94 g_hash_table_destroy (parser->pid_hash);
96 while (!list_is_empty (&parser->errlist)) {
97 n = (VgErrorListNode *) list_unlink_head (&parser->errlist);
99 if (n->err_cur)
100 vg_error_free (n->err_cur);
102 g_free (n);
105 g_free (parser);
109 static int
110 vg_error_get_state (VgErrorParser *parser, pid_t pid)
112 VgErrorListNode *n;
114 if (!(n = g_hash_table_lookup (parser->pid_hash, GINT_TO_POINTER (pid))))
115 return VG_ERROR_PARSER_STATE_INIT;
117 return n->state;
120 static void
121 vg_error_set_state (VgErrorParser *parser, pid_t pid, int state)
123 VgErrorListNode *n;
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"));
127 return;
130 n->state = state;
133 static VgError *
134 vg_error_new (VgErrorParser *parser, pid_t pid, time_stamp_t *stamp)
136 VgErrorListNode *n;
137 VgError *err;
139 if (!(n = g_hash_table_lookup (parser->pid_hash, GINT_TO_POINTER (pid)))) {
140 n = g_new (VgErrorListNode, 1);
141 n->pid = pid;
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) {
146 return n->err_cur;
149 err = g_new (VgError, 1);
150 memcpy (&err->stamp, stamp, sizeof (err->stamp));
151 err->pid = pid;
152 err->thread = (vgthread_t) -1;
153 err->summary = NULL;
155 n->err_cur = err;
156 n->summ_cur = NULL;
157 n->summ_tail = (VgErrorSummary *) &err->summary;
158 n->stack_tail = NULL;
159 n->state = VG_ERROR_PARSER_STATE_NEW_ERROR;
161 return err;
164 static void
165 vg_error_pop (VgErrorParser *parser, pid_t pid)
167 VgErrorListNode *n;
169 if (!(n = g_hash_table_lookup (parser->pid_hash, GINT_TO_POINTER (pid))))
170 return;
172 if (n->err_cur) {
173 if (n->err_cur->summary) {
174 parser->error_cb (parser, n->err_cur, parser->user_data);
175 } else {
176 d(fprintf (stderr, "Incomplete valgrind error being popped??\n"));
177 g_free (n->err_cur);
179 } else {
180 d(fprintf (stderr, "VgErrorParser stack underflow??\n"));
183 n->state = VG_ERROR_PARSER_STATE_INIT;
185 n->err_cur = NULL;
186 n->summ_cur = NULL;
187 n->summ_tail = NULL;
188 n->stack_tail = NULL;
191 static void
192 vg_error_pop_all (VgErrorParser *parser)
194 VgErrorListNode *n;
196 n = (VgErrorListNode *) parser->errlist.head;
197 while (n->next != NULL) {
198 vg_error_pop (parser, n->pid);
199 n = n->next;
203 static void
204 vg_error_summary_append (VgErrorParser *parser, pid_t pid, const char *report, int len)
206 VgErrorSummary *summary;
207 VgErrorListNode *n;
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"));
211 return;
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)
230 VgErrorStack *stack;
231 VgErrorListNode *n;
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"));
235 return NULL;
238 stack = g_new (VgErrorStack, 1);
239 stack->next = NULL;
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;
252 return stack;
256 vg_error_parser_step (VgErrorParser *parser)
258 register char *inptr;
259 char *start, *end;
260 time_stamp_t stamp;
261 vgthread_t thread;
262 unsigned int num;
263 int state, ret;
264 VgError *err;
265 Parser *priv;
266 pid_t pid;
268 priv = (Parser *) parser;
270 if ((ret = parser_fill (priv)) == 0) {
271 vg_error_pop_all (parser);
272 return 0;
273 } else if (ret == -1) {
274 return -1;
277 start = inptr = (char *)priv->inptr;
279 while (inptr < (char *)priv->inend) {
280 *priv->inend = '\n';
281 while (*inptr != '\n')
282 inptr++;
284 d(fprintf (stderr, "parser_step: '%.*s'\n", inptr - start, start));
286 if (inptr == (char *)priv->inend)
287 break;
289 if (start[0] != '=' || start[1] != '=') {
290 d(fprintf (stderr, "Unexpected data received from valgrind: '%.*s'\n", inptr - start, start));
291 inptr++;
292 start = inptr;
293 continue;
296 stamp.year = 0;
298 start += 2;
299 if ((num = strtoul (start, &end, 10)) == 0 || end == start || *end != '=') {
300 /* possible time stamp */
301 if (*end != '-') {
302 d(fprintf (stderr, "Invalid pid or time stamp received from valgrind: '%.*s'\n", end - start, start));
303 inptr++;
304 start = inptr;
305 continue;
308 stamp.year = num;
309 start = end + 1;
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));
313 inptr++;
314 start = inptr;
315 continue;
318 stamp.month = num;
319 start = end + 1;
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));
323 inptr++;
324 start = inptr;
325 continue;
328 stamp.day = num;
329 start = end + 1;
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));
333 inptr++;
334 start = inptr;
335 continue;
338 stamp.hour = num;
339 start = end + 1;
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));
343 inptr++;
344 start = inptr;
345 continue;
348 stamp.min = num;
349 start = end + 1;
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));
353 inptr++;
354 start = inptr;
355 continue;
358 stamp.sec = num;
359 start = end + 1;
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));
363 inptr++;
364 start = inptr;
365 continue;
368 stamp.msec = num;
369 start = end + 1;
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));
373 inptr++;
374 start = inptr;
375 continue;
377 } else {
378 pid = num;
381 if (end[0] != '=' || end[1] != '=') {
382 d(fprintf (stderr, "Unexpected data received from valgrind: '%.*s'\n", (inptr - start) + 2, start - 2));
383 inptr++;
384 start = inptr;
385 continue;
388 start = end + 3;
390 *inptr = '\0';
391 if ((state = vg_error_get_state (parser, pid)) & VG_ERROR_PARSER_STATE_WARNING) {
392 while (*start == ' ')
393 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);
400 inptr++;
401 start = inptr;
402 continue;
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 == ' ')
413 start++;
416 if (start < inptr) {
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) {
430 start += 7;
431 thread = strtoul (start, &end, 10);
432 if (*end != ':') {
433 start -= 7;
434 vg_error_summary_append (parser, pid, start, inptr - start);
435 vg_error_set_state (parser, pid, VG_ERROR_PARSER_STATE_PARTIAL_ERROR);
436 } else {
437 err->thread = thread;
439 } else {
440 vg_error_summary_append (parser, pid, start, inptr - start);
441 vg_error_set_state (parser, pid, VG_ERROR_PARSER_STATE_PARTIAL_ERROR);
444 } else {
445 /* another summary, a new stack frame, or end-of-info (ie. a blank line) */
446 while (*start == ' ')
447 start++;
449 if (start < inptr) {
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);
465 } else {
466 VgErrorStack *stack;
468 stack = vg_error_stack_new (parser, pid);
469 stack->where = *start == 'a' ? VG_WHERE_AT : VG_WHERE_BY;
470 start += 3;
472 if (*start == '<') {
473 /* unknown address */
474 stack->addr = VG_STACK_ADDR_UNKNOWN;
476 while (start < inptr && *start != '>')
477 start++;
479 if (*start == '>')
480 start++;
481 } else {
482 /* symbol address in hex */
483 stack->addr = strtoul (start, (char **) &end, 16);
484 start = end;
486 if (*start == ':')
487 start++;
490 if (*start == ' ')
491 start++;
493 if (strncmp (start, "??? ", 4) == 0) {
494 stack->symbol = NULL;
495 start += 3;
496 } else if (*start == '(') {
497 /* not a c/c++ program, hence no symbol */
498 stack->symbol = NULL;
499 start--;
500 } else {
501 /* symbol name */
502 end = start;
503 while (end < inptr && *end != ' ' && *end != '(')
504 end++;
506 if (*end == '(') {
507 /* symbol name has a param list - probably a c++ symbol */
508 while (end < inptr && *end != ')')
509 end++;
511 if (*end == ')')
512 end++;
515 stack->symbol = vg_strndup (start, end - start);
516 start = end;
519 if (*start == ' ')
520 start++;
522 if (*start == '(') {
523 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;
529 start += 7;
530 } else if (strncmp (start, "in ", 3) == 0) {
531 /* (in /lib/foo.so) */
532 stack->type = VG_STACK_OBJECT;
533 start += 3;
534 } else {
535 stack->type = VG_STACK_SOURCE;
538 end = start;
539 while (end < inptr && *end != ':' && *end != ')')
540 end++;
542 /* src filename or shared object */
543 if (stack->type == VG_STACK_SOURCE) {
544 stack->info.src.filename = vg_strndup (start, end - start);
546 start = end;
547 if (*start++ == ':')
548 stack->info.src.lineno = strtoul (start, (char **) &end, 10);
549 else
550 stack->info.src.lineno = 0;
551 } else {
552 stack->info.object = vg_strndup (start, end - start);
555 start = end;
558 } else {
559 /* end-of-info (ie. a blank line) */
560 vg_error_pop (parser, pid);
564 inptr++;
565 start = inptr;
568 priv->inptr = (unsigned char *)start;
570 return 1;
574 void
575 vg_error_parser_flush (VgErrorParser *parser)
577 VgErrorListNode *n;
579 n = (VgErrorListNode *) parser->errlist.head;
580 while (n->next != NULL) {
581 if (n->err_cur) {
582 if (n->state == VG_ERROR_PARSER_STATE_PARTIAL_ERROR) {
583 vg_error_pop (parser, n->pid);
584 } else {
585 g_free (n->err_cur);
586 n->err_cur = NULL;
590 n = n->next;
595 static void
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);
601 else
602 vg_strfree (stack->info.object);
604 g_free (stack);
607 static void
608 vg_error_summary_free (VgErrorSummary *summary)
610 VgErrorStack *frame, *next;
612 vg_strfree (summary->report);
614 frame = summary->frames;
615 while (frame != NULL) {
616 next = frame->next;
617 vg_error_stack_free (frame);
618 frame = next;
621 g_free (summary);
625 void
626 vg_error_free (VgError *err)
628 VgErrorSummary *summary, *next;
630 if (err == NULL)
631 return;
633 summary = err->summary;
634 while (summary != NULL) {
635 next = summary->next;
636 vg_error_summary_free (summary);
637 summary = next;
640 g_free (err);
644 static void
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);
662 else
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);
669 } else {
670 int in;
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);
678 static void
679 vg_error_summary_to_string (VgErrorSummary *summary, int indent, GString *str)
681 time_stamp_t *stamp = &summary->parent->stamp;
682 VgErrorStack *s;
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');
696 s = summary->frames;
697 while (s != NULL) {
698 vg_error_stack_to_string (s, str);
699 s = s->next;
704 void
705 vg_error_to_string (VgError *err, GString *str)
707 VgErrorSummary *s;
708 int indent = 0;
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);
720 s = err->summary;
721 while (s != NULL) {
722 vg_error_summary_to_string (s, indent, str);
723 indent = indent || s->frames;
724 s = s->next;
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);