2 This file is part of the NoBug debugging library.
5 2010, Christian Thaeter <ct@pipapo.org>
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, contact Christian Thaeter <ct@pipapo.org>.
25 #include <search.h> /* should use a hash instead, someday */
28 #define NOBUG_LIBNOBUG_C
32 Note about threading: the lookup tree is build at startup before spawning threads, after that it
33 is only used readonly, no locks needed.
37 #define FNV_64_PRIME ((uint64_t)0x100000001b3ULL)
38 #define FNV_64_BASE ((uint64_t)1099511628211)
41 nobug_fnv_64a_buf (const void *buf
, size_t len
, uint64_t hval
)
43 const unsigned char *bp
= (const unsigned char *)buf
;
44 const unsigned char *be
= bp
+ len
;
47 hval
^= (uint64_t)*bp
++;
57 nobug_fnv_64a_str (const char* str
, uint64_t hval
)
59 const unsigned char *bp
= (const unsigned char *)str
;
63 hval
^= (uint64_t)*bp
++;
74 cmp_uint64 (const void *a_
, const void *b_
)
76 const uint64_t* a
= (const uint64_t*) a_
;
77 const uint64_t* b
= (const uint64_t*) b_
;
79 return *a
<*b
?-1:*a
>*b
?1:0;
84 cmp_nobug_coverage_record (const void *a_
, const void *b_
)
86 const struct nobug_coverage_record
* a
= (const struct nobug_coverage_record
*) a_
;
87 const struct nobug_coverage_record
* b
= (const struct nobug_coverage_record
*) b_
;
89 return cmp_uint64 (&a
->hash
, &b
->hash
);
95 /* the last found failure when parsing a log back in */
96 static uint64_t lastfail
= 0;
98 /* all records parsed from log */
99 void* nobug_coverage_tree
= NULL
;
102 nobug_coverage_cleanup (void)
104 tdestroy (nobug_coverage_tree
, free
);
111 log - stream to parse in
114 nobug_coverage_parse_log (FILE* log
)
116 static struct nobug_coverage_record
* record
= NULL
;
119 while (fgets (buf
, 1024, log
))
121 /* malloc node, malloc errors are gracefully ignored, the application will prolly notice them anyways */
122 record
= record
?record
:malloc(sizeof(*record
)); /* TODO mpool, no destroy needed then? */
124 char* start
= strstr (buf
, "COVERAGE:");
126 if (start
&& record
&& 2 == sscanf (start
, "COVERAGE: %*[^:]:%*[^:]: %*[^:]: %*[^:]: INJECT %"SCNx64
": %c", &record
->hash
, &record
->state
))
128 struct nobug_coverage_record
** r
= tsearch (record
, &nobug_coverage_tree
, cmp_nobug_coverage_record
);
130 if (r
&& *r
== record
)
132 if (record
->state
== 'F')
133 lastfail
= record
->hash
;
144 // reads log from the env var $NOBUG_COVERAGE delimited by spaces, comma or semicolon a single - means stdin
147 nobug_coverage_init (const struct nobug_context context
)
149 char* loglist
= getenv ("NOBUG_COVERAGE");
150 if (loglist
&& (loglist
= strdup (loglist
)))
152 atexit (nobug_coverage_cleanup
);
154 for (const char* log
= strtok (loglist
, " ;,"); log
; log
= strtok (NULL
, " ;,"))
156 if (!strcmp (log
, "-"))
158 nobug_log (&nobug_flag_nobug
, LOG_DEBUG
, "NOBUG", context
, " reading coverage log from stdin");
159 nobug_coverage_parse_log (stdin
);
163 FILE* f
= fopen (log
, "r");
166 nobug_log (&nobug_flag_nobug
, LOG_DEBUG
, "NOBUG", context
, " reading coverage log from %s", log
);
167 nobug_coverage_parse_log (f
);
172 nobug_log (&nobug_flag_nobug
, LOG_ERR
, "NOBUG", context
, " Could not open log %s: %s",
173 log
, strerror(errno
));
179 if (nobug_coverage_tree
&& !lastfail
)
181 nobug_log (&nobug_flag_nobug
, LOG_NOTICE
, "NOBUG", context
, " COVERAGE CHECKING FINISHED");
185 nobug_log (&nobug_flag_nobug
, LOG_NOTICE
, "NOBUG", context
, " COVERAGE CHECKING ACTIVE");
188 if (!nobug_coverage_tree
)
190 /* one fakerecord at least, to make the nobug_coverage_tree appear not empty */
191 struct nobug_coverage_record
* fakerecord
= malloc (sizeof(*fakerecord
));
192 fakerecord
->hash
= 0;
193 fakerecord
->state
= 0;
194 tsearch (fakerecord
, &nobug_coverage_tree
, cmp_nobug_coverage_record
);
200 struct nobug_coverage_record
201 nobug_coverage_check (void)
203 if (!nobug_coverage_tree
)
204 return (struct nobug_coverage_record
){0,0};
206 static void* btbuf
[512];
207 struct nobug_coverage_record record
;
209 int len
= backtrace (btbuf
, 512);
210 len
-= 2; /* coverage_check() and backtrace() itself */
212 record
.hash
= FNV_64_BASE
;
214 record
.hash
= nobug_fnv_64a_buf (btbuf
, len
*sizeof(void*), record
.hash
);
215 struct nobug_coverage_record
** r
= tfind (&record
, &nobug_coverage_tree
, cmp_nobug_coverage_record
);
217 if (r
&& (record
.hash
== lastfail
|| (*r
)->state
== 'P'))
233 // c-file-style: "gnu"
234 // indent-tabs-mode: nil