improve the 'literate' source docs for the resource tracker
[nobug.git] / src / nobug_coverage.c
blobf4be4675c250a5fff2ddc19214b557532308106e
1 /*
2 This file is part of the NoBug debugging library.
4 Copyright (C)
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>.
21 #include <inttypes.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <search.h> /* should use a hash instead, someday */
26 #include <errno.h>
28 #define NOBUG_LIBNOBUG_C
29 #include "nobug.h"
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)
40 uint64_t
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;
45 while (bp < be)
47 hval ^= (uint64_t)*bp++;
48 hval *= FNV_64_PRIME;
51 return hval;
55 #if 0
56 uint64_t
57 nobug_fnv_64a_str (const char* str, uint64_t hval)
59 const unsigned char *bp = (const unsigned char *)str;
60 if (bp)
61 while (*bp)
63 hval ^= (uint64_t)*bp++;
64 hval *= FNV_64_PRIME;
67 return hval;
69 #endif
73 static int
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;
83 static int
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;
101 void
102 nobug_coverage_cleanup (void)
104 tdestroy (nobug_coverage_tree, free);
109 parse log,
111 log - stream to parse in
113 void
114 nobug_coverage_parse_log (FILE* log)
116 static struct nobug_coverage_record* record = NULL;
117 char buf[1024];
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;
134 record = NULL;
139 free (record);
144 // reads log from the env var $NOBUG_COVERAGE delimited by spaces, comma or semicolon a single - means stdin
146 void
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);
161 else
163 FILE* f = fopen (log, "r");
164 if (f)
166 nobug_log (&nobug_flag_nobug, LOG_DEBUG, "NOBUG", context, " reading coverage log from %s", log);
167 nobug_coverage_parse_log (f);
168 fclose (f);
170 else
172 nobug_log (&nobug_flag_nobug, LOG_ERR, "NOBUG", context, " Could not open log %s: %s",
173 log, strerror(errno));
177 free (loglist);
179 if (nobug_coverage_tree && !lastfail)
181 nobug_log (&nobug_flag_nobug, LOG_NOTICE, "NOBUG", context, " COVERAGE CHECKING FINISHED");
183 else
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'))
218 record.state = 'P';
219 else
220 record.state = 'F';
222 return record;
231 // Local Variables:
232 // mode: C
233 // c-file-style: "gnu"
234 // indent-tabs-mode: nil
235 // End: