Updated Spanish translation
[anjuta-git-plugin.git] / plugins / valgrind / vgrule.c
blobe7bb9cf18872ef5a7d8a73233d169203ca5c4bee
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>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <ctype.h>
39 #include "vgstrpool.h"
40 #include "vgrule.h"
41 #include "vgio.h"
44 static const char *vg_caller_types[] = { "fun", "obj", NULL };
47 const char *
48 vg_caller_type_to_name (vgcaller_t type)
50 return vg_caller_types[type];
54 vgcaller_t
55 vg_caller_type_from_name (const char *name)
57 int i;
59 for (i = 0; i < VG_CALLER_LAST; i++) {
60 if (!strcmp (vg_caller_types[i], name))
61 break;
64 return i;
68 VgCaller *
69 vg_caller_new (vgcaller_t type, const char *name)
71 VgCaller *caller;
73 caller = g_new (VgCaller, 1);
74 caller->next = NULL;
75 caller->type = type;
76 caller->name = g_strdup (name);
78 return caller;
82 void
83 vg_caller_free (VgCaller *caller)
85 if (caller == NULL)
86 return;
88 g_free (caller->name);
89 g_free (caller);
93 static const char *vg_rule_types[] = {
94 "Addr1", "Addr2", "Addr4", "Addr8", "Cond", "Free", "Leak",
95 "Param", "PThread", "Value1", "Value2", "Value4", "Value8", NULL
99 const char *
100 vg_rule_type_to_name (vgrule_t type)
102 return vg_rule_types[type];
106 vgrule_t
107 vg_rule_type_from_name (const char *name)
109 int i;
111 for (i = 0; i < VG_RULE_LAST; i++) {
112 if (!strcmp (vg_rule_types[i], name))
113 break;
116 return i;
121 vg_rule_type_from_report (const char *report, vgrule_t *type, char **syscall)
123 const char *inptr, *call;
124 unsigned int size;
125 char *end;
127 if (syscall)
128 *syscall = NULL;
130 /* FIXME: How can we auto-detect PThread errors? */
131 if (!strncmp (report, "Conditional ", 12)) {
132 *type = VG_RULE_COND;
133 return TRUE;
134 } else if (!strncmp (report, "Syscall param ", 14)) {
135 *type = VG_RULE_PARAM;
137 if (syscall) {
138 call = report + 14;
139 if ((inptr = strchr (call, ' ')) != NULL)
140 *syscall = g_strndup (call, (inptr - call));
143 return TRUE;
144 } else if (!strcmp (report, "Invalid free() / delete / delete[]")) {
145 *type = VG_RULE_FREE;
146 return TRUE;
147 } else if ((inptr = strstr (report, " are still reachable in loss record "))) {
148 *type = VG_RULE_LEAK;
149 return TRUE;
150 } else if (!strncmp (report, "Invalid read of size ", 21)) {
151 inptr = report + 21;
152 size = strtoul (inptr, &end, 10);
153 switch (size) {
154 case 1:
155 *type = VG_RULE_ADDR1;
156 break;
157 case 2:
158 *type = VG_RULE_ADDR2;
159 break;
160 case 4:
161 *type = VG_RULE_ADDR4;
162 break;
163 case 8:
164 *type = VG_RULE_ADDR8;
165 break;
166 default:
167 return FALSE;
170 return TRUE;
171 } else if ((inptr = strstr (report, "value of size "))) {
172 /* only reason we strstr here instead of strncmp'ing
173 * against "Use of uninitialised value of size " is
174 * that someone may eventually convince the valgrind
175 * authors to use the Americanised spelling and then
176 * our strncmp would fail */
177 inptr += 14;
178 size = strtoul (inptr, &end, 10);
179 switch (size) {
180 case 1:
181 *type = VG_RULE_VALUE1;
182 break;
183 case 2:
184 *type = VG_RULE_VALUE2;
185 break;
186 case 4:
187 *type = VG_RULE_VALUE4;
188 break;
189 case 8:
190 *type = VG_RULE_VALUE8;
191 break;
192 default:
193 return FALSE;
196 return TRUE;
199 return FALSE;
203 VgRule *
204 vg_rule_new (vgrule_t type, const char *name)
206 VgRule *rule;
208 rule = g_new (VgRule, 1);
209 rule->name = g_strdup (name);
210 rule->tools = NULL;
211 rule->type = type;
212 rule->syscall = NULL;
213 rule->callers = NULL;
215 return rule;
219 static VgTool *
220 parse_tool_list (unsigned char *tool_list)
222 VgTool *tail, *n, *list = NULL;
223 register unsigned char *inptr;
224 unsigned char *start;
226 tail = (VgTool *) &list;
228 start = inptr = tool_list;
229 while (*inptr) {
230 while (*inptr && *inptr != ',')
231 inptr++;
233 if (*inptr == ',')
234 *inptr++ = '\0';
236 n = g_new (VgTool, 1);
237 n->next = NULL;
238 n->name = vg_strdup ((char *)start);
240 tail->next = n;
241 tail = n;
243 start = inptr;
246 return list;
250 void
251 vg_rule_add_tool (VgRule *rule, const char *tool)
253 VgTool *tail, *n;
255 n = g_new (VgTool, 1);
256 n->next = NULL;
257 n->name = vg_strdup (tool);
259 tail = (VgTool *) &rule->tools;
260 while (tail->next != NULL)
261 tail = tail->next;
263 tail->next = n;
267 void
268 vg_rule_free (VgRule *rule)
270 VgCaller *n, *nn;
271 VgTool *s, *sn;
273 if (rule == NULL)
274 return;
276 g_free (rule->name);
277 g_free (rule->syscall);
279 s = rule->tools;
280 while (s != NULL) {
281 sn = s->next;
282 vg_strfree (s->name);
283 g_free (s);
284 s = sn;
287 n = rule->callers;
288 while (n != NULL) {
289 nn = n->next;
290 vg_caller_free (n);
291 n = nn;
294 g_free (rule);
298 enum {
299 VG_RULE_PARSER_STATE_INIT = 0,
300 VG_RULE_PARSER_STATE_COMMENT = (1 << 0),
301 VG_RULE_PARSER_STATE_RULE_NAME = (1 << 1),
302 VG_RULE_PARSER_STATE_RULE_TYPE = (1 << 2),
303 VG_RULE_PARSER_STATE_RULE_SYSCALL = (1 << 3),
304 VG_RULE_PARSER_STATE_RULE_CALLER = (1 << 4),
307 VgRuleParser *
308 vg_rule_parser_new (int fd, VgRuleParserRuleCallback rule_cb, void *user_data)
310 VgRuleParser *parser;
312 parser = g_new (VgRuleParser, 1);
313 parser_init ((Parser *) parser, fd);
315 parser->linebuf = g_malloc (128);
316 parser->lineptr = parser->linebuf;
317 parser->lineleft = 128;
319 parser->current = NULL;
320 parser->tail = NULL;
322 parser->rule_cb = rule_cb;
323 parser->user_data = user_data;
325 parser->state = VG_RULE_PARSER_STATE_INIT;
327 return parser;
330 void
331 vg_rule_parser_free (VgRuleParser *parser)
333 if (parser == NULL)
334 return;
336 g_free (parser->linebuf);
337 g_free (parser);
341 #define backup_linebuf(parser, start, len) G_STMT_START { \
342 if (parser->lineleft <= len) { \
343 unsigned int llen, loff; \
345 llen = loff = parser->lineptr - parser->linebuf; \
346 llen = llen ? llen : 1; \
348 while (llen < (loff + len + 1)) \
349 llen <<= 1; \
351 parser->linebuf = g_realloc (parser->linebuf, llen); \
352 parser->lineptr = parser->linebuf + loff; \
353 parser->lineleft = llen - loff; \
356 memcpy (parser->lineptr, start, len); \
357 parser->lineptr += len; \
358 parser->lineleft -= len; \
359 } G_STMT_END
361 static void
362 vg_parser_parse_caller (VgRuleParser *parser)
364 register unsigned char *inptr;
365 VgCaller *caller;
366 vgcaller_t type;
368 *parser->lineptr = ':';
369 inptr = parser->linebuf;
370 while (*inptr != ':')
371 inptr++;
373 if (inptr == parser->lineptr) {
374 *parser->lineptr = '\0';
375 fprintf (stderr, "Invalid caller field encountered: '%s'\n", parser->linebuf);
376 return;
379 *inptr++ = '\0';
380 if ((type = vg_caller_type_from_name ((char *)parser->linebuf)) == VG_CALLER_LAST) {
381 fprintf (stderr, "Invalid caller type encountered: '%s'\n", parser->linebuf);
382 return;
385 *parser->lineptr = '\0';
386 caller = vg_caller_new (type, (char *)inptr);
388 parser->tail->next = caller;
389 parser->tail = caller;
393 vg_rule_parser_step (VgRuleParser *parser)
395 register unsigned char *inptr;
396 unsigned char *start;
397 Parser *priv;
398 int ret;
400 priv = (Parser *) parser;
402 if ((ret = parser_fill (priv)) == 0) {
403 return 0;
404 } else if (ret == -1) {
405 return -1;
408 start = inptr = priv->inptr;
409 *priv->inend = '\n';
411 while (inptr < priv->inend) {
412 if (parser->state == VG_RULE_PARSER_STATE_INIT) {
413 if (*inptr == '#') {
414 parser->state = VG_RULE_PARSER_STATE_COMMENT;
415 inptr++;
416 } else if (*inptr == '{') {
417 parser->state = VG_RULE_PARSER_STATE_RULE_NAME;
418 parser->current = vg_rule_new (0, NULL);
419 parser->tail = (VgCaller *) &parser->current->callers;
420 inptr++;
421 } else {
422 /* eat any whitespace?? */
423 while (*inptr == ' ' || *inptr == '\t')
424 inptr++;
426 if (*inptr == '#' || *inptr == '{')
427 continue;
429 /* unknown, skip this line??? */
430 *priv->inend = '\n';
431 while (*inptr != '\n')
432 inptr++;
434 if (inptr == priv->inend)
435 break;
437 inptr++;
439 continue;
443 comment:
444 if (parser->state & VG_RULE_PARSER_STATE_COMMENT) {
445 /* eat this line... in the future we may want to save comment lines? */
446 *priv->inend = '\n';
447 while (*inptr != '\n')
448 inptr++;
450 if (inptr == priv->inend)
451 break;
453 inptr++;
455 parser->state &= ~VG_RULE_PARSER_STATE_COMMENT;
457 if (parser->state == VG_RULE_PARSER_STATE_INIT)
458 continue;
461 if (parser->lineptr == parser->linebuf) {
462 /* haven't come to the start of the actual rule data line yet */
464 /* eat lwsp */
465 *priv->inend = '\0';
466 while (isspace ((int) *inptr))
467 inptr++;
469 if (inptr == priv->inend)
470 break;
472 if (*inptr == '#') {
473 /* comment within the rule brackets */
474 parser->state |= VG_RULE_PARSER_STATE_COMMENT;
475 inptr++;
477 goto comment;
481 if (*inptr == '}') {
482 inptr++;
484 if (parser->state == VG_RULE_PARSER_STATE_RULE_CALLER) {
485 parser->rule_cb (parser, parser->current, parser->user_data);
486 } else {
487 fprintf (stderr, "Encountered unexpected '}'\n");
488 vg_rule_free (parser->current);
491 parser->state = VG_RULE_PARSER_STATE_INIT;
493 parser->current = NULL;
494 parser->tail = NULL;
496 parser->lineleft += (parser->lineptr - parser->linebuf);
497 parser->lineptr = parser->linebuf;
499 continue;
502 *priv->inend = '\n';
503 start = inptr;
504 while (*inptr != '\n')
505 inptr++;
507 backup_linebuf (parser, start, (inptr - start));
509 if (inptr == priv->inend)
510 break;
512 switch (parser->state) {
513 case VG_RULE_PARSER_STATE_RULE_NAME:
514 *parser->lineptr = '\0';
515 parser->current->name = g_strdup ((char *)parser->linebuf);
516 parser->state = VG_RULE_PARSER_STATE_RULE_TYPE;
517 break;
518 case VG_RULE_PARSER_STATE_RULE_TYPE:
519 /* The newer valgrind (1.9.5) suppression format contains
520 * a list of tool names preceeding the suppression type:
522 * Memcheck,Addrcheck:Param
524 * The older format (1.0.x) contained just the type name.
527 /* trim tailing whitespc */
528 start = parser->lineptr - 1;
529 while (start > parser->linebuf && isspace ((int) *start))
530 start--;
532 start[1] = '\0';
534 /* does this suppression rule contain a list
535 of tools that it is meant for? */
536 while (start > parser->linebuf && *start != ':')
537 start--;
539 if (*start == ':') {
540 /* why yes, yes it does... */
541 *start++ = '\0';
542 parser->current->tools = parse_tool_list (parser->linebuf);
545 parser->current->type = vg_rule_type_from_name ((char *)start);
546 g_assert (parser->current->type != VG_RULE_LAST);
547 if (parser->current->type == VG_RULE_PARAM)
548 parser->state = VG_RULE_PARSER_STATE_RULE_SYSCALL;
549 else
550 parser->state = VG_RULE_PARSER_STATE_RULE_CALLER;
551 break;
552 case VG_RULE_PARSER_STATE_RULE_SYSCALL:
553 parser->current->syscall = g_strndup ((char *)start, (inptr - start));
554 parser->state = VG_RULE_PARSER_STATE_RULE_CALLER;
555 break;
556 case VG_RULE_PARSER_STATE_RULE_CALLER:
557 vg_parser_parse_caller (parser);
558 break;
561 parser->lineleft += (parser->lineptr - parser->linebuf);
562 parser->lineptr = parser->linebuf;
564 inptr++;
567 priv->inptr = inptr;
569 return 1;
573 void
574 vg_rule_parser_flush (VgRuleParser *parser)
581 vg_suppressions_file_write_header (int fd, const char *summary)
583 GString *string;
585 string = g_string_new ("##----------------------------------------------------------------------##\n\n");
586 g_string_append (string, "# ");
587 g_string_append (string, summary);
588 g_string_append (string, "\n\n");
590 /* format specification header */
591 g_string_append (string, "# Format of this file is:\n");
592 g_string_append (string, "# {\n");
593 g_string_append (string, "# name_of_suppression\n");
594 g_string_append (string, "# tool_name:supp_kind\n");
595 g_string_append (string, "# (optional extra info for some suppression types)\n");
596 g_string_append (string, "# caller0 name, or /name/of/so/file.so\n");
597 g_string_append (string, "# caller1 name, or ditto\n");
598 g_string_append (string, "# (optionally: caller2 name)\n");
599 g_string_append (string, "# (optionally: caller3 name)\n");
600 g_string_append (string, "# }\n");
601 g_string_append (string, "#\n");
602 g_string_append (string, "# For Memcheck, the supp_kinds are:\n");
603 g_string_append (string, "#\n");
604 g_string_append (string, "# Param Value1 Value2 Value4 Value8\n");
605 g_string_append (string, "# Free Addr1 Addr2 Addr4 Addr8 Leak\n");
606 g_string_append (string, "# Cond (previously known as Value0)\n");
607 g_string_append (string, "#\n");
608 g_string_append (string, "# and the optional extra info is:\n");
609 g_string_append (string, "# if Param: name of system call param\n");
610 g_string_append (string, "# if Free: name of free-ing fn)\n\n");
612 if (vg_write (fd, string->str, string->len) == -1) {
613 g_string_free (string, TRUE);
614 return -1;
617 g_string_free (string, TRUE);
619 return 0;
624 vg_suppressions_file_append_rule (int fd, VgRule *rule)
626 GString *string;
627 VgCaller *caller;
628 VgTool *tool;
630 string = g_string_new ("{\n ");
631 g_string_append (string, rule->name);
632 g_string_append (string, "\n ");
634 tool = rule->tools;
635 if (tool != NULL) {
636 while (tool != NULL) {
637 g_string_append (string, tool->name);
638 if (tool->next)
639 g_string_append_c (string, ',');
640 tool = tool->next;
643 g_string_append_c (string, ':');
646 g_string_append (string, vg_rule_types[rule->type]);
648 if (rule->type == VG_RULE_PARAM) {
649 g_string_append (string, "\n ");
650 g_string_append (string, rule->syscall);
653 caller = rule->callers;
654 while (caller != NULL) {
655 g_string_append_printf (string, "\n %s:%s", vg_caller_types[caller->type], caller->name);
656 caller = caller->next;
659 g_string_append (string, "\n}\n");
661 if (vg_write (fd, string->str, string->len) == -1) {
662 g_string_free (string, TRUE);
663 return -1;
666 g_string_free (string, TRUE);
668 return 0;
672 #if TEST
673 static void
674 my_rule_cb (VgRuleParser *parser, VgRule *rule, void *user_data)
676 vg_suppressions_file_append_rule (1, rule);
679 int main (int argc, char **argv)
681 VgRuleParser *parser;
682 int fd;
684 if ((fd = open (argv[1], O_RDONLY)) == -1)
685 return 0;
687 vg_suppressions_file_write_header (1, "Errors to suppress by default for glibc 2.1.3");
689 parser = vg_rule_parser_new (fd, my_rule_cb, NULL);
691 while (vg_rule_parser_step (parser) > 0)
694 vg_rule_parser_free (parser);
696 close (fd);
698 return 0;
700 #endif