• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 25935296376

15 May 2026 06:29PM UTC coverage: 92.258% (+0.004%) from 92.254%
25935296376

push

github

nickg
Handle real values with integer formats in vpi_get_value

32 of 43 new or added lines in 2 files covered. (74.42%)

623 existing lines in 14 files now uncovered.

78015 of 84562 relevant lines covered (92.26%)

641925.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.57
/src/util.c
1
//
2
//  Copyright (C) 2011-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#if defined(__MINGW32__)
19
#define WINVER 0x0A00
20
#define _WIN32_WINNT 0x0A00
21
#include <windows.h>
22
#include <fileapi.h>
23
#include <psapi.h>
24
#include <io.h>
25
#include <shlwapi.h>
26
#endif
27

28
#include "util.h"
29
#include "array.h"
30
#include "cpustate.h"
31
#include "debug.h"
32
#include "diag.h"
33
#include "option.h"
34
#include "printf.h"
35
#include "thread.h"
36

37
#include <stdlib.h>
38
#include <stdio.h>
39
#include <stdarg.h>
40
#include <errno.h>
41
#include <string.h>
42
#include <stdbool.h>
43
#include <signal.h>
44
#include <stdint.h>
45
#include <inttypes.h>
46
#include <math.h>
47
#include <unistd.h>
48
#include <ctype.h>
49
#include <assert.h>
50
#include <limits.h>
51
#include <time.h>
52
#include <libgen.h>
53
#include <fcntl.h>
54

55
#include <sys/types.h>
56
#include <sys/stat.h>
57
#include <sys/time.h>
58
#ifdef HAVE_SYS_PTRACE_H
59
#include <sys/ptrace.h>
60
#endif
61
#ifdef __APPLE__
62
#include <sys/sysctl.h>
63
#include <libproc.h>
64
#endif
65
#ifdef __FreeBSD__
66
#include <sys/sysctl.h>
67
#endif
68
#ifndef __MINGW32__
69
#include <sys/mman.h>
70
#include <sys/wait.h>
71
#include <sys/resource.h>
72
#include <sys/file.h>
73
#include <sys/ioctl.h>
74
#include <termios.h>
75
#endif
76

77
#ifdef HAVE_PTHREAD
78
#include <pthread.h>
79
#endif
80

81
#ifdef HAVE_SYS_PRCTL_H
82
#include <sys/prctl.h>
83
#endif
84

85
#ifdef __CYGWIN__
86
#include <process.h>
87
#endif
88

89
#if defined(HAVE_UCONTEXT_H)
90
#include <ucontext.h>
91
#elif defined(HAVE_SYS_UCONTEXT_H)
92
#include <sys/ucontext.h>
93
#endif
94

95
#include "thirdparty/sha1.h"
96

97
#define HUGE_PAGE_SIZE  0x200000
98

99
#define POOL_MIN_ALIGN  sizeof(double)
100
#define POOL_PAGE_4K    12
101
#define POOL_PAGE_1M    20
102
#define POOL_PAGE_MIN   POOL_PAGE_4K
103
#define POOL_PAGE_MAX   POOL_PAGE_1M
104

105
#if ASAN_ENABLED
106
#define POOL_REDZONE 16
107
#else
108
#define POOL_REDZONE 0
109
#endif
110

111
static void show_bug_report(void);
112

113
typedef struct _fault_handler fault_handler_t;
114
typedef struct _pool_page pool_page_t;
115

116
struct text_buf {
117
   char  *buf;
118
   size_t alloc;
119
   size_t len;
120
};
121

122
struct _fault_handler {
123
   fault_handler_t *next;
124
   fault_fn_t       fn;
125
   void            *context;
126
};
127

128
typedef struct _pool_page {
129
   pool_page_t *next;
130
   size_t       alloc;
131
   size_t       size;
132
   uint8_t      data[0];
133
} page_header_t;
134

135
typedef struct _mem_pool {
136
   pool_page_t *pages;
137
   size_t       pageshift;
138
} mem_pool_t;
139

140
static bool             want_color = false;
141
static bool             want_utf8 = false;
142
static message_style_t  message_style = MESSAGE_FULL;
143
static sig_atomic_t     crashing = SIG_ATOMIC_MAX;
144
static int              term_width = 0;
145
static void            *ctrl_c_arg = NULL;
146
static fault_handler_t *fault_handlers = NULL;
147

148
#ifdef __MINGW32__
149
static UINT win32_codepage = 0;
150
#endif
151

152
static void (*ctrl_c_fn)(void *) = NULL;
153

154
void *xmalloc(size_t size)
18,189,854✔
155
{
156
   void *p = malloc(size);
18,189,854✔
157
   if (p == NULL)
18,189,854✔
UNCOV
158
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
159
   return p;
18,189,854✔
160
}
161

162
void *xmalloc_flex(size_t fixed, size_t nelems, size_t size)
14,170,654✔
163
{
164
   size_t bytes;
14,170,654✔
165
   if (__builtin_mul_overflow(nelems, size, &bytes))
14,170,654✔
166
      fatal_trace("array size overflow: requested %zd * %zd bytes",
167
                  nelems, size);
168

169
   return xmalloc(fixed + bytes);
14,170,654✔
170
}
171

172
void *xmalloc_array(size_t nelems, size_t size)
1,678,390✔
173
{
174
   return xmalloc_flex(0, nelems, size);
1,678,390✔
175
}
176

177
void *xcalloc(size_t size)
13,125,615✔
178
{
179
   void *p = calloc(1, size);
13,125,615✔
180
   if (p == NULL)
13,125,615✔
UNCOV
181
      fatal("memory exhausted (calloc %lu)", (long unsigned)size);
×
182
   return p;
13,125,615✔
183
}
184

185
void *xcalloc_flex(size_t fixed, size_t nelems, size_t size)
10,387,967✔
186
{
187
   size_t bytes;
10,387,967✔
188
   if (__builtin_mul_overflow(nelems, size, &bytes))
10,387,967✔
189
      fatal_trace("array size overflow: requested %zd * %zd bytes",
190
                  nelems, size);
191

192
   return xcalloc(fixed + bytes);
10,387,967✔
193
}
194

195
void *xcalloc_array(size_t nelems, size_t size)
1,672,330✔
196
{
197
   return xcalloc_flex(0, nelems, size);
1,672,330✔
198
}
199

200
void *xrealloc(void *ptr, size_t size)
4,492,553✔
201
{
202
   ptr = realloc(ptr, size);
4,492,553✔
203
   if (ptr == NULL)
4,492,553✔
UNCOV
204
      fatal("memory exhausted (realloc %lu)", (long unsigned)size);
×
205
   return ptr;
4,492,553✔
206
}
207

208
void *xrealloc_array(void *ptr, size_t nelems, size_t size)
3,523,392✔
209
{
210
   size_t bytes;
3,523,392✔
211
   if (__builtin_mul_overflow(nelems, size, &bytes))
3,523,392✔
212
      fatal_trace("array size overflow: requested %zd * %zd bytes",
213
                  nelems, size);
214

215
   return xrealloc(ptr, bytes);
3,523,392✔
216
}
217

218
void *xrealloc_flex(void *ptr, size_t fixed, size_t nelems, size_t size)
42,835✔
219
{
220
   size_t bytes;
42,835✔
221
   if (__builtin_mul_overflow(nelems, size, &bytes))
42,835✔
222
      fatal_trace("array size overflow: requested %zd * %zd bytes",
223
                  nelems, size);
224

225
   return xrealloc(ptr, bytes + fixed);
42,835✔
226
}
227

228
char *xstrdup(const char *str)
519,536✔
229
{
230
   char *copy = strdup(str);
519,536✔
231
   if (copy == NULL)
519,536✔
UNCOV
232
      fatal("memory exhausted (strdup)");
×
233
   return copy;
519,536✔
234
}
235

236
char *xstrndup(const char *str, size_t n)
63✔
237
{
238
   char *copy = strndup(str, n);
63✔
239
   if (copy == NULL)
63✔
UNCOV
240
      fatal("memory exhausted (strndup)");
×
241
   return copy;
63✔
242
}
243

244
char *xvasprintf(const char *fmt, va_list ap)
461,411✔
245
{
246
   char *strp = NULL;
461,411✔
247
   if (vasprintf(&strp, fmt, ap) < 0)
461,411✔
UNCOV
248
      fatal("memory exhausted (vasprintf)");
×
249
   return strp;
461,411✔
250
}
251

252
char *xasprintf(const char *fmt, ...)
215,311✔
253
{
254
   va_list ap;
215,311✔
255
   va_start(ap, fmt);
215,311✔
256
   char *strp = xvasprintf(fmt, ap);
215,311✔
257
   va_end(ap);
215,311✔
258
   return strp;
215,311✔
259
}
260

261
void errorf(const char *fmt, ...)
64✔
262
{
263
   diag_t *d = diag_new(DIAG_ERROR, NULL);
64✔
264
   va_list ap;
64✔
265
   va_start(ap, fmt);
64✔
266
   diag_vprintf(d, fmt, ap);
64✔
267
   va_end(ap);
64✔
268
   diag_emit(d);
64✔
269
}
64✔
270

271
void warnf(const char *fmt, ...)
42✔
272
{
273
   diag_t *d = diag_new(DIAG_WARN, NULL);
42✔
274
   va_list ap;
42✔
275
   va_start(ap, fmt);
42✔
276
   diag_vprintf(d, fmt, ap);
42✔
277
   va_end(ap);
42✔
278
   diag_emit(d);
42✔
279
}
42✔
280

281
void notef(const char *fmt, ...)
2,661✔
282
{
283
   diag_t *d = diag_new(DIAG_NOTE, NULL);
2,661✔
284
   va_list ap;
2,661✔
285
   va_start(ap, fmt);
2,661✔
286
   diag_vprintf(d, fmt, ap);
2,661✔
287
   va_end(ap);
2,661✔
288
   diag_emit(d);
2,661✔
289
}
2,661✔
290

291
void debugf(const char *fmt, ...)
2,499✔
292
{
293
   diag_t *d = diag_new(DIAG_DEBUG, NULL);
2,499✔
294
   va_list ap;
2,499✔
295
   va_start(ap, fmt);
2,499✔
296
   diag_vprintf(d, fmt, ap);
2,499✔
297
   va_end(ap);
2,499✔
298
   diag_emit(d);
2,499✔
299
}
2,499✔
300

301
bool color_terminal(void)
4,057✔
302
{
303
   return want_color;
4,057✔
304
}
305

306
bool utf8_terminal(void)
1,152✔
307
{
308
   return want_utf8;
1,152✔
309
}
310

311
void print_centred(const char *text)
4✔
312
{
313
   if (term_width == 0)
4✔
314
      fputs(text, stdout);
4✔
315
   else {
UNCOV
316
      const int pad = (term_width - strlen(text)) / 2;
×
UNCOV
317
      printf("%*s%s", pad, "", text);
×
318
   }
319
}
4✔
320

321
void fatal_exit(int status)
57✔
322
{
323
   async_barrier();
57✔
324

325
   if (atomic_load(&crashing) != SIG_ATOMIC_MAX)
57✔
326
      _exit(status);   // Exit during crash
×
327
   else if (!thread_attached() || thread_id() != 0)
57✔
UNCOV
328
      _exit(status);
×
329
   else
330
      exit(status);
57✔
331
}
332

333
void error_at(const loc_t *loc, const char *fmt, ...)
754✔
334
{
335
   diag_t *d = diag_new(DIAG_ERROR, loc);
754✔
336

337
   va_list ap;
754✔
338
   va_start(ap, fmt);
754✔
339
   diag_vprintf(d, fmt, ap);
754✔
340
   va_end(ap);
754✔
341

342
   diag_emit(d);
754✔
343
}
754✔
344

345
void warn_at(const loc_t *loc, const char *fmt, ...)
107✔
346
{
347
   diag_t *d = diag_new(DIAG_WARN, loc);
107✔
348

349
   va_list ap;
107✔
350
   va_start(ap, fmt);
107✔
351
   diag_vprintf(d, fmt, ap);
107✔
352
   va_end(ap);
107✔
353

354
   diag_emit(d);
107✔
355
}
107✔
356

357
void note_at(const loc_t *loc, const char *fmt, ...)
12,256✔
358
{
359
   diag_t *d = diag_new(DIAG_NOTE, loc);
12,256✔
360

361
   va_list ap;
12,256✔
362
   va_start(ap, fmt);
12,256✔
363
   diag_vprintf(d, fmt, ap);
12,256✔
364
   va_end(ap);
12,256✔
365

366
   diag_emit(d);
12,256✔
367
}
12,256✔
368

369
void fatal_at(const loc_t *loc, const char *fmt, ...)
×
370
{
UNCOV
371
   diag_t *d = diag_new(DIAG_FATAL, loc);
×
372
   diag_suppress(d, false);
×
373

374
   va_list ap;
×
375
   va_start(ap, fmt);
×
UNCOV
376
   diag_vprintf(d, fmt, ap);
×
377
   va_end(ap);
×
378

379
   diag_set_consumer(NULL, NULL);
×
UNCOV
380
   diag_emit(d);
×
UNCOV
381
   fatal_exit(EXIT_FAILURE);
×
382
}
383

384
void fatal(const char *fmt, ...)
48✔
385
{
386
   diag_t *d = diag_new(DIAG_FATAL, NULL);
48✔
387
   diag_suppress(d, false);
48✔
388

389
   va_list ap;
48✔
390
   va_start(ap, fmt);
48✔
391
   diag_vprintf(d, fmt, ap);
48✔
392
   va_end(ap);
48✔
393

394
   diag_set_consumer(NULL, NULL);
48✔
395
   diag_emit(d);
48✔
396
   fatal_exit(EXIT_FAILURE);
48✔
397
}
398

399
void fatal_trace(const char *fmt, ...)
400
{
UNCOV
401
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
402
   diag_suppress(d, false);
×
403

404
   va_list ap;
×
405
   va_start(ap, fmt);
×
UNCOV
406
   diag_vprintf(d, fmt, ap);
×
407
   va_end(ap);
×
408

UNCOV
409
   diag_set_consumer(NULL, NULL);
×
410
   diag_emit(d);
×
411

412
   show_stacktrace();
×
413

414
   show_bug_report();
×
415

UNCOV
416
   fatal_exit(EXIT_FAILURE);
×
417
}
418

419
void fatal_errno(const char *fmt, ...)
4✔
420
{
421
   diag_t *d = diag_new(DIAG_FATAL, NULL);
4✔
422
   diag_suppress(d, false);
4✔
423

424
   va_list ap;
4✔
425
   va_start(ap, fmt);
4✔
426
   diag_vprintf(d, fmt, ap);
4✔
427
   diag_printf(d, ": %s", last_os_error());
4✔
428
   va_end(ap);
4✔
429

430
   diag_set_consumer(NULL, NULL);
4✔
431
   diag_emit(d);
4✔
432
   fatal_exit(EXIT_FAILURE);
4✔
433
}
434

435
const char *last_os_error(void)
4✔
436
{
437
#ifdef __MINGW32__
438
   static __thread LPSTR mbuf = NULL;
439

440
   if (mbuf != NULL)
441
      LocalFree(mbuf);
442

443
   FormatMessage(
444
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
445
      | FORMAT_MESSAGE_IGNORE_INSERTS,
446
      NULL,
447
      GetLastError(),
448
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
449
      (LPSTR)&mbuf, 0, NULL);
450

451
   return mbuf;
452
#else
453
   return strerror(errno);
4✔
454
#endif
455
}
456

UNCOV
457
static void trace_one_frame(uintptr_t pc, const char *module,
×
458
                            const char *srcfile, const char *symbol,
459
                            unsigned lineno, unsigned colno,
460
                            ptrdiff_t disp, frame_kind_t kind)
461
{
462
   ostream_t *os = nvc_stderr();
×
463

464
   nvc_fprintf(os, "[$green$%p$$] ", (void *)pc);
×
465
   if (kind == FRAME_LIB)
×
466
      nvc_fprintf(os, "($red$%s$$) ", module);
×
467
   if (srcfile != NULL)
×
468
      nvc_fprintf(os, "%s:%d ", srcfile, lineno);
×
469
   if (symbol != NULL) {
×
470
      nvc_fprintf(os, "$yellow$%s$$", symbol);
×
UNCOV
471
      if (srcfile == NULL && disp != 0)
×
472
         nvc_fprintf(os, "$yellow$+0x%"PRIxPTR"$$", disp);
×
473
   }
474
   if (kind == FRAME_VHDL)
×
UNCOV
475
      nvc_fprintf(os, " $magenta$[VHDL]$$");
×
UNCOV
476
   fprintf(stderr, "\n");
×
477

478
#ifndef __MINGW32__
479
   if (srcfile != NULL) {
×
480
      FILE *f = fopen(srcfile, "r");
×
481
      if (f != NULL) {
×
482
         char *line LOCAL = NULL;
×
483
         size_t linesz = 0;
×
484
         for (int i = 0, len; i < lineno + 1
×
485
                 && (len = getline(&line, &linesz, f)) != -1; i++) {
×
UNCOV
486
            if (i < lineno - 2)
×
487
               continue;
×
488

489
            if (len <= 1)
×
490
               continue;
×
UNCOV
491
            else if (line[len - 1] == '\n')
×
492
               line[len - 1] = '\0';
×
493

UNCOV
494
            if (i == lineno - 1)
×
495
               nvc_fprintf(os, "$cyan$$bold$-->$$ $cyan$%s$$\n", line);
×
496
            else
497
               nvc_fprintf(os, "    $cyan$%s$$\n", line);
×
498
         }
UNCOV
499
         fclose(f);
×
500
      }
501
   }
502
#endif
UNCOV
503
}
×
504

505
__attribute__((noinline))
506
void show_stacktrace(void)
×
507
{
508
   debug_info_t *di = debug_capture();
×
509

510
   const int nframes = debug_count_frames(di);
×
UNCOV
511
   for (int n = 1; n < nframes; n++) {
×
512
      const debug_frame_t *f = debug_get_frame(di, n);
×
513

514
      for (debug_inline_t *inl = f->inlined; inl != NULL; inl = inl->next)
×
UNCOV
515
         trace_one_frame(f->pc, f->module, inl->srcfile, inl->symbol,
×
516
                         inl->lineno, inl->colno, f->disp, f->kind);
×
517

UNCOV
518
      trace_one_frame(f->pc, f->module, f->srcfile, f->symbol, f->lineno,
×
UNCOV
519
                      f->colno, f->disp, f->kind);
×
520

521
   }
522

UNCOV
523
   debug_free(di);
×
524

525
#if defined __linux__ && !defined HAVE_LIBDW && !defined HAVE_LIBDWARF
526
   nvc_fprintf(nvc_stderr(), "\n$cyan$Hint: you can get better stack traces by "
527
               "installing the libdw-dev package and reconfiguring$$\n");
528
#endif
529

UNCOV
530
   fflush(stderr);
×
UNCOV
531
}
×
532

533
static void show_bug_report(void)
534
{
535
#ifndef DEBUG
536
   extern const char version_string[];
537
   nvc_fprintf(nvc_stderr(), "\n$!red$%s ["TARGET_SYSTEM"]\n\n"
538
               "Please report this bug at "PACKAGE_BUGREPORT"$$\n\n",
539
               version_string);
540
   fflush(stderr);
541
#endif
542
}
543

544
#ifdef __MINGW32__
545

546
static const char *exception_name(DWORD code)
547
{
548
   switch (code) {
549
   case EXCEPTION_ACCESS_VIOLATION:
550
      return "EXCEPTION_ACCESS_VIOLATION";
551
   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
552
      return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
553
   case EXCEPTION_BREAKPOINT:
554
      return "EXCEPTION_BREAKPOINT";
555
   case EXCEPTION_DATATYPE_MISALIGNMENT:
556
      return "EXCEPTION_DATATYPE_MISALIGNMENT";
557
   case EXCEPTION_ILLEGAL_INSTRUCTION:
558
      return "EXCEPTION_ILLEGAL_INSTRUCTION";
559
   case EXCEPTION_IN_PAGE_ERROR:
560
      return "EXCEPTION_IN_PAGE_ERROR";
561
   case EXCEPTION_INT_DIVIDE_BY_ZERO:
562
      return "EXCEPTION_INT_DIVIDE_BY_ZERO";
563
   case EXCEPTION_INT_OVERFLOW:
564
      return "EXCEPTION_INT_OVERFLOW";
565
   case EXCEPTION_PRIV_INSTRUCTION:
566
      return "EXCEPTION_PRIV_INSTRUCTION";
567
   case EXCEPTION_STACK_OVERFLOW:
568
      return "EXCEPTION_STACK_OVERFLOW";
569
   }
570

571
   return "???";
572
}
573

574
WINAPI
575
static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
576
{
577
   DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
578
   PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
579

580
#if defined ARCH_ARM64
581
   DWORD64 ip = ExceptionInfo->ContextRecord->Pc;
582
#elif defined ARCH_X86_64
583
   DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
584
#else
585
   DWORD ip = ExceptionInfo->ContextRecord->Eip;
586
#endif
587

588
   if (code == EXCEPTION_ACCESS_VIOLATION)
589
      addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
590

591
   fprintf(stderr, "\n%s*** Caught exception %x (%s)",
592
           want_color ? "\033[31m\033[1m" : "",
593
           (int)code, exception_name(code));
594

595
   switch (code) {
596
   case EXCEPTION_ACCESS_VIOLATION:
597
   case EXCEPTION_ILLEGAL_INSTRUCTION:
598
      fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
599
      break;
600
   }
601

602
   fprintf(stderr, " ***%s\n\n", want_color ? "\033[0m" : "");
603
   fflush(stderr);
604

605
#ifdef __WIN64
606
   if (code != EXCEPTION_STACK_OVERFLOW)
607
      show_stacktrace();
608
#endif
609

610
  return EXCEPTION_EXECUTE_HANDLER;
611
}
612

613
#else
614

615
#ifndef __SANITIZE_THREAD__
616
static const char *signame(int sig, siginfo_t *info)
×
617
{
UNCOV
618
   switch (sig) {
×
619
   case SIGSEGV:
×
620
#ifdef __linux__
621
      switch (info->si_code) {
×
622
      case SEGV_MAPERR: return "SEGV_MAPERR";
UNCOV
623
      case SEGV_ACCERR: return "SEGV_ACCERR";
×
UNCOV
624
      default: return "SIGSEGV";
×
625
      }
626
#else
627
      return "SIGSEGV";
628
#endif
629
   case SIGABRT: return "SIGABRT";
630
   case SIGILL: return "SIGILL";
×
631
   case SIGFPE: return "SIGFPE";
×
632
   case SIGUSR1: return "SIGUSR1";
×
633
   case SIGUSR2: return "SIGUSR2";
×
634
   case SIGBUS: return "SIGBUS";
×
635
   case SIGINT: return "SIGINT";
×
UNCOV
636
   case SIGTRAP: return "SIGTRAP";
×
UNCOV
637
   default: return "???";
×
638
   }
639
}
640

641
static void print_fatal_signal(int sig, siginfo_t *info, struct cpu_state *cpu)
×
642
{
643
   static volatile __thread sig_atomic_t recurse = 0;
×
644

645
   if (recurse++ > 1) {
×
UNCOV
646
      signal(SIGABRT, SIG_DFL);
×
UNCOV
647
      abort();
×
648
   }
649

650
   char buf[512], *p = buf, *s = buf, *end = buf + ARRAY_LEN(buf);
×
UNCOV
651
   p += checked_sprintf(p, end - p, "\n%s*** Caught signal %d (%s)%s",
×
652
                        want_color ? "\033[31m\033[1m" : "",
×
653
                        sig, signame(sig, info),
654
                        recurse > 1 ? " inside signal handler" : "");
×
655

UNCOV
656
   switch (sig) {
×
UNCOV
657
   case SIGSEGV:
×
658
   case SIGILL:
659
   case SIGFPE:
660
   case SIGBUS:
661
      p += checked_sprintf(p, end - p, " [address=%p, ip=%p]",
×
UNCOV
662
                           info->si_addr, (void*)cpu->pc);
×
UNCOV
663
      break;
×
664
   }
665

666
   p += checked_sprintf(p, end - p, " ***%s\n\n", want_color ? "\033[0m" : "");
×
667

668
   for (int n; s < p && (n = write(STDERR_FILENO, s, p - s)) > 0; s += n);
×
669

670
   if (sig != SIGUSR1 && !atomic_cas(&crashing, SIG_ATOMIC_MAX, thread_id())) {
×
UNCOV
671
      sleep(60);
×
672
      _exit(EXIT_FAILURE);
×
673
   }
UNCOV
674
}
×
675
#endif  // !__SANITIZE_THREAD__
676

677
static __thread struct cpu_state *thread_regs = NULL;
678

679
static void signal_handler(int sig, siginfo_t *info, void *context)
×
680
{
681
   ucontext_t *uc = (ucontext_t*)context;
×
UNCOV
682
   struct cpu_state cpu;
×
683
   fill_cpu_state(&cpu, uc);
×
684

UNCOV
685
   struct cpu_state *req;
×
686
   if (sig == SIGUSR2 && (req = atomic_load(&thread_regs)) != NULL) {
×
687
      // Fill in registers for capture_registers
688
      *req = cpu;
×
UNCOV
689
      atomic_store(&thread_regs, NULL);
×
690
      return;
×
691
   }
692
   else if (sig == SIGINT) {
×
693
      void (*fn)(void *) = atomic_load(&ctrl_c_fn);
×
694
      if (fn != NULL) {
×
UNCOV
695
         (*fn)(ctrl_c_arg);
×
UNCOV
696
         return;
×
697
      }
698
   }
699

700
#ifdef __SANITIZE_THREAD__
701
   abort();
702
#else
703

UNCOV
704
   for (fault_handler_t *f = fault_handlers; f; f = f->next)
×
705
      (*f->fn)(sig, info->si_addr, &cpu, f->context);
×
706

707
   print_fatal_signal(sig, info, &cpu);
×
708

709
   show_stacktrace();
×
710

711
   show_bug_report();
×
712

UNCOV
713
   if (sig != SIGUSR1)
×
UNCOV
714
      _exit(2);
×
715
#endif  // !__SANITIZE_THREAD__
716
}
717
#endif  // ! __MINGW32__
718

719
void register_signal_handlers(void)
8,894✔
720
{
721
#ifdef __MINGW32__
722
   SetUnhandledExceptionFilter(win32_exception_handler);
723
#else
724

725
   struct sigaction sa = {
8,894✔
726
      .sa_sigaction = signal_handler,
727
      .sa_flags = SA_RESTART | SA_SIGINFO
728
   };
729
   sigemptyset(&sa.sa_mask);
8,894✔
730

731
#ifndef __SANITIZE_THREAD__
732
   sigaction(SIGSEGV, &sa, NULL);
8,894✔
733
   sigaction(SIGUSR1, &sa, NULL);
8,894✔
734
   sigaction(SIGFPE, &sa, NULL);
8,894✔
735
   sigaction(SIGBUS, &sa, NULL);
8,894✔
736
   sigaction(SIGILL, &sa, NULL);
8,894✔
737
   sigaction(SIGABRT, &sa, NULL);
8,894✔
738
   sigaction(SIGTRAP, &sa, NULL);
8,894✔
739
#endif  // !__SANITIZE_THREAD__
740
   sigaction(SIGUSR2, &sa, NULL);
8,894✔
741
#endif  // !__MINGW32__
742
}
8,894✔
743

744
#ifdef __MINGW32__
745
static BOOL win32_ctrl_c_handler(DWORD fdwCtrlType)
746
{
747
   switch (fdwCtrlType) {
748
   case CTRL_C_EVENT:
749
      {
750
         void (*fn)(void *) = atomic_load(&ctrl_c_fn);
751
         if (fn != NULL)
752
            (*fn)(ctrl_c_arg);
753
         return TRUE;
754
      }
755

756
   default:
757
      return FALSE;
758
   }
759
}
760
#endif
761

762
void set_ctrl_c_handler(void (*fn)(void *), void *arg)
10,840✔
763
{
764
   ctrl_c_arg = arg;
10,840✔
765
   atomic_store(&ctrl_c_fn, fn);
10,840✔
766

767
   if (fn != NULL) {
10,840✔
768
#ifndef __MINGW32__
769
      struct sigaction sa = {};
5,420✔
770
      sa.sa_sigaction = signal_handler;
5,420✔
771
      sigemptyset(&sa.sa_mask);
5,420✔
772
      sa.sa_flags = SA_RESTART | SA_SIGINFO;
5,420✔
773

774
      sigaction(SIGINT, &sa, NULL);
5,420✔
775
#else
776
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, TRUE))
777
         fatal_trace("SetConsoleCtrlHandler");
778
#endif
779
   }
780
   else {
781
#ifndef __MINGW32__
782
      struct sigaction sa = {};
5,420✔
783
      sa.sa_handler = SIG_DFL;
5,420✔
784
      sigaction(SIGINT, &sa, NULL);
5,420✔
785
#else
786
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
787
         fatal_trace("SetConsoleCtrlHandler");
788
#endif
789
   }
790
}
10,840✔
791

792
#ifdef __MINGW32__
793
static void restore_win32_codepage(void)
794
{
795
   assert(win32_codepage != 0);
796
   SetConsoleOutputCP(win32_codepage);
797
}
798
#endif
799

800
void term_init(void)
8,894✔
801
{
802
   const char *nvc_colors = getenv("NVC_COLORS");
8,894✔
803
   const char *term = getenv("TERM") ?: "";
8,894✔
804

805
   static const char *term_blacklist[] = {
8,894✔
806
      "dumb"
807
   };
808

809
   spin_wait();  // Dummy, to force linking thread.c
8,894✔
810

811
   bool is_tty = isatty(STDERR_FILENO);
8,894✔
812

813
#ifdef __MINGW32__
814
   if (!is_tty) {
815
      // Handle running under MinTty
816
      HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
817
      const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
818
      FILE_NAME_INFO *nameinfo = malloc(size);
819
      if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
820
         fatal_errno("GetFileInformationByHandle");
821

822
      if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
823
           || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
824
          && wcsstr(nameinfo->FileName, L"pty") != NULL)
825
         is_tty = true;
826

827
      free(nameinfo);
828
   }
829
#endif
830

831
   if (nvc_colors && strcmp(nvc_colors, "always") == 0)
8,894✔
832
      want_color = true;
4✔
833
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
8,890✔
UNCOV
834
      want_color = false;
×
835
   else {
836
      want_color = is_tty;
8,890✔
837

838
      if (want_color && (term != NULL)) {
8,890✔
839
         for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
×
840
            if (strcmp(term, term_blacklist[i]) == 0) {
×
UNCOV
841
               want_color = false;
×
UNCOV
842
               break;
×
843
            }
844
         }
845
      }
846
   }
847

848
#ifdef __MINGW32__
849
   HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
850
   DWORD mode;
851
   if (GetConsoleMode(hConsole, &mode)) {
852
      mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
853
      if (!SetConsoleMode(hConsole, mode))
854
         want_color = false;
855

856
      CONSOLE_SCREEN_BUFFER_INFO info;
857
      if (GetConsoleScreenBufferInfo(hConsole, &info))
858
         term_width = info.dwSize.X;
859
      else
860
         term_width = 80;
861
   }
862
#else
863
   if (is_tty) {
8,894✔
864
      // Try to find the terminal size with tcgetwinsize or TIOCGWINSZ
UNCOV
865
      term_width = 80;
×
866
#if defined HAVE_TCGETWINSIZE
867
      struct winsize ws;
868
      if (tcgetwinsize(STDIN_FILENO, &ws) == 0)
869
         term_width = ws.ws_col;
870
#elif defined TIOCGWINSZ
871
      struct winsize ws;
×
UNCOV
872
      if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
×
UNCOV
873
         term_width = ws.ws_col;
×
874
#endif
875
   }
876
#endif
877

878
#ifndef __MINGW32__
879
   // Assume the terminal is expecting UTF-8 by default
880
   want_utf8 = true;
8,894✔
881

882
   const char *lang = getenv("LANG");
8,894✔
883
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
8,894✔
UNCOV
884
      want_utf8 = false;
×
885
#else
886
   win32_codepage = GetConsoleOutputCP();
887
   if (win32_codepage == 65001)
888
      want_utf8 = true;
889
   else if (win32_codepage != 28591) {
890
      SetConsoleOutputCP(28591);
891
      atexit(restore_win32_codepage);
892
   }
893
#endif
894

895
   // Diagnostics are printed to stderr and explicitly flushed
896
   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
8,894✔
897
}
8,894✔
898

899
int terminal_width(void)
30,725✔
900
{
901
   return term_width;
30,725✔
902
}
903

904
const char *ordinal_str(int n)
30✔
905
{
906
   switch (n) {
30✔
907
   case 1: return "first";
908
   case 2: return "second";
6✔
UNCOV
909
   case 3: return "third";
×
910
   default:
×
911
      {
912
         static char buf[16];
×
913
         if (n > 20 && n % 10 == 1)
×
914
            checked_sprintf(buf, sizeof(buf), "%dst", n);
×
915
         else if (n > 20 && n % 10 == 2)
×
916
            checked_sprintf(buf, sizeof(buf), "%dnd", n);
×
UNCOV
917
         else if (n > 20 && n % 10 == 2)
×
918
            checked_sprintf(buf, sizeof(buf), "%drd", n);
×
919
         else
UNCOV
920
            checked_sprintf(buf, sizeof(buf), "%dth", n);
×
921
         return buf;
922
      }
923
   }
924
}
925

926
char *null_terminate(const uint8_t *data, size_t len)
1,717✔
927
{
928
   char *cstr = xmalloc(len + 1);
1,717✔
929
   if (data != NULL)
1,717✔
930
      memcpy(cstr, data, len);
1,717✔
931
   else
UNCOV
932
      assert(len == 0);
×
933
   cstr[len] = '\0';
1,717✔
934
   return cstr;
1,717✔
935
}
936

937
char toupper_iso88591(unsigned char ch)
4,582,934✔
938
{
939
   if (ch >= 'a' && ch <= 'z')
4,582,934✔
940
      return ch - 'a' + 'A';
2,423,075✔
941
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
2,159,859✔
942
      return ch - 0x20;
39✔
943
   else
944
      return ch;
2,159,820✔
945
}
946

947
char tolower_iso88591(unsigned char ch)
25,464,063✔
948
{
949
   if (ch >= 'A' && ch <= 'Z')
25,464,063✔
950
      return ch + 'a' - 'A';
1,212,530✔
951
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
24,251,533✔
952
      return ch + 0x20;
240✔
953
   else
954
      return ch;
24,251,293✔
955
}
956

957
bool isprint_iso88591(unsigned char ch)
395,341✔
958
{
959
   return (ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff);
395,341✔
960
}
961

962
bool isspace_iso88591(unsigned char ch)
237,743✔
963
{
964
   return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f'
237,743✔
965
      || ch == '\r' || ch == 0xa0;
237,743✔
966
}
967

968
bool isdigit_iso88591(unsigned char ch)
36,529✔
969
{
970
   return ch >= '0' && ch <= '9';
36,529✔
971
}
972

973
bool isupper_iso88591(unsigned char ch)
3,026✔
974
{
975
   return (ch >= 'A' && ch <= 'Z')
3,026✔
976
      || (ch >= 0xc0 && ch <= 0xd6)
3,026✔
977
      || (ch >= 0xd8 && ch <= 0xde);
3,026✔
978
}
979

980
bool isalpha_iso88591(unsigned char ch)
2,065,124✔
981
{
982
   return (ch >= 'A' && ch <= 'Z')
2,065,124✔
983
      || (ch >= 'a' && ch <= 'z')
2,065,124✔
984
      || (ch >= 0xc0 && ch <= 0xd6)
985
      || (ch >= 0xd8 && ch <= 0xde)
986
      || (ch >= 0xe0 && ch <= 0xf6)
987
      || (ch >= 0xf8 && ch <= 0xfe);
2,065,124✔
988
}
989

990
bool isalnum_iso88591(unsigned char ch)
503,259✔
991
{
992
   return isalpha_iso88591(ch) || (ch >= '0' && ch <= '9');
503,259✔
993
}
994

995
int next_power_of_2(int n)
1,673,065✔
996
{
997
   n--;
1,673,065✔
998
   n |= n >> 1;
1,673,065✔
999
   n |= n >> 2;
1,673,065✔
1000
   n |= n >> 4;
1,673,065✔
1001
   n |= n >> 8;
1,673,065✔
1002
   n |= n >> 16;
1,673,065✔
1003
   n++;
1,673,065✔
1004
   return n;
1,673,065✔
1005
}
1006

1007
int ilog2(int64_t n)
22,427✔
1008
{
1009
   if (n <= 1)
22,427✔
1010
      return 0;
1011
   else {
1012
      int r = 0;
1013
      int64_t c = 1;
1014
      while (c < n) {
98,574✔
1015
         r += 1;
76,198✔
1016
         c <<= 1;
76,198✔
1017
      }
1018
      return r;
22,376✔
1019
   }
1020
}
1021

1022
bool ipow_safe(int64_t x, int64_t y, int64_t *result)
415✔
1023
{
1024
   assert(y >= 0);
415✔
1025
   int overflow = 0, xo = 0;
1026
   int64_t r = 1;
1027
   while (y) {
1,558✔
1028
      if (y & 1)
1,143✔
1029
         overflow |= xo || __builtin_mul_overflow(r, x, &r);
653✔
1030
      y >>= 1;
1,143✔
1031
      xo |= __builtin_mul_overflow(x, x, &x);
1,143✔
1032
   }
1033
   *result = r;
415✔
1034
   return !overflow;
415✔
1035
}
1036

1037
int64_t ipow(int64_t x, int64_t y)
2✔
1038
{
1039
   int64_t result;
2✔
1040
   if (!ipow_safe(x, y, &result))
2✔
1041
      DEBUG_ONLY(fatal_trace("integer overflow in ipow"));
1042

1043
   return result;
2✔
1044
}
1045

1046
#if !ASAN_ENABLED
1047
static long nvc_page_size(void)
1048
{
1049
#ifdef __MINGW32__
1050
   SYSTEM_INFO si;
1051
   GetSystemInfo(&si);
1052
   return si.dwPageSize;
1053
#else
1054
   return sysconf(_SC_PAGESIZE);
1055
#endif
1056
}
1057
#endif
1058

1059
void nvc_munmap(void *ptr, size_t length)
26,520✔
1060
{
1061
#if ASAN_ENABLED
1062
   free(ptr);
26,520✔
1063
#elif !defined __MINGW32__
1064
   if (munmap(ptr, length) != 0)
1065
      fatal_errno("munmap");
1066
#else
1067
   if (!VirtualFree(ptr, length, MEM_DECOMMIT))
1068
      fatal_errno("VirtualFree");
1069
#endif
1070
}
26,520✔
1071

1072
void *nvc_memalign(size_t align, size_t sz)
88,567✔
1073
{
1074
#if ASAN_ENABLED
1075
   void *ptr;
88,567✔
1076
   if (posix_memalign(&ptr, align, sz) != 0)
177,134✔
UNCOV
1077
      fatal_errno("posix_memalign");
×
1078

1079
   memset(ptr, '\0', sz);
88,567✔
1080
   return ptr;
88,567✔
1081
#else
1082
   assert(is_power_of_2(align));
1083
   const size_t mapalign = MAX(align, nvc_page_size());
1084
   const size_t mapsz = ALIGN_UP(sz + mapalign - 1, mapalign);
1085

1086
#if defined __MINGW32__
1087
   void *ptr = VirtualAlloc(NULL, mapsz, MEM_COMMIT | MEM_RESERVE,
1088
                            PAGE_READWRITE);
1089
   if (ptr == NULL)
1090
      fatal_errno("VirtualAlloc");
1091
#else
1092
   void *ptr = mmap(NULL, mapsz, PROT_READ | PROT_WRITE,
1093
                    MAP_PRIVATE | MAP_ANON, -1, 0);
1094
   if (ptr == MAP_FAILED)
1095
      fatal_errno("mmap failed to allocate %zu bytes", sz);
1096
#endif
1097

1098
   void *aligned = ALIGN_UP(ptr, align);
1099
   void *limit = aligned + sz;
1100

1101
   if (align > nvc_page_size()) {
1102
      const size_t low_waste = aligned - ptr;
1103
      const size_t high_waste = ptr + mapsz - limit;
1104
      assert(low_waste + high_waste == align);
1105

1106
      if (low_waste > 0) nvc_munmap(ptr, low_waste);
1107
      if (high_waste > 0) nvc_munmap(limit, high_waste);
1108
   }
1109

1110
   return aligned;
1111
#endif
1112
}
1113

1114
void nvc_memprotect(void *ptr, size_t length, mem_access_t prot)
90,108✔
1115
{
1116
#if defined __MINGW32__
1117
   static const int map[] = {
1118
      PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ,
1119
      PAGE_EXECUTE_READWRITE
1120
   };
1121
   DWORD old_prot;
1122
   if (length > 0 && !VirtualProtect(ptr, length, map[prot], &old_prot))
1123
      fatal_errno("VirtualProtect");
1124
#else
1125
#if ASAN_ENABLED
1126
   // LeakSanitizer will not detect leaks in regions mapped read-only
1127
   if (prot == MEM_RO || prot == MEM_NONE)
90,108✔
1128
      return;
1129
#endif
1130
   static const int map[] = {
5,701✔
1131
      PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
1132
      PROT_READ | PROT_WRITE | PROT_EXEC
1133
   };
1134
   if (mprotect(ptr, length, map[prot]) < 0)
5,701✔
UNCOV
1135
      fatal_errno("mprotect");
×
1136
#endif
1137
}
1138

1139
void nvc_decommit(void *ptr, size_t length)
23,071✔
1140
{
1141
#if defined __MINGW32__
1142
   if (length > 0 && !VirtualFree(ptr, length, MEM_DECOMMIT))
1143
      fatal_errno("VirtualFree");
1144
#elif defined __linux__
1145
   if (madvise(ptr, length, MADV_DONTNEED) != 0)
23,071✔
UNCOV
1146
      fatal_errno("madvise: MADV_DONTNEED");
×
1147
#endif
1148
}
23,071✔
1149

1150
void *map_huge_pages(size_t align, size_t sz)
26,560✔
1151
{
1152
#ifdef __linux__
1153
   if (sz >= HUGE_PAGE_SIZE) {
26,560✔
1154
      const size_t mapsz = ALIGN_UP(sz, HUGE_PAGE_SIZE);
25,985✔
1155
      void *mem = nvc_memalign(MAX(HUGE_PAGE_SIZE, align), mapsz);
25,985✔
1156

1157
      if (madvise(mem, mapsz, MADV_HUGEPAGE) < 0)
25,985✔
UNCOV
1158
         warnf("madvise: MADV_HUGEPAGE: %s", last_os_error());
×
1159

1160
      return mem;
25,985✔
1161
   }
1162
#endif
1163

1164
   return nvc_memalign(align, sz);
575✔
1165
}
1166

1167
void *map_jit_pages(size_t align, size_t sz)
5,701✔
1168
{
1169
#ifdef __APPLE__
1170
   void *ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
1171
                    MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
1172
   if (ptr == MAP_FAILED)
1173
      fatal_errno("mmap failed to allocate %zu bytes for executable code", sz);
1174
#else
1175
   void *ptr = map_huge_pages(align, sz);
5,701✔
1176
   nvc_memprotect(ptr, sz, MEM_RWX);
5,701✔
1177
#endif
1178

1179
   return ptr;
5,701✔
1180
}
1181

1182
int checked_vsprintf(char *buf, int len, const char *fmt, va_list ap)
1,294,866✔
1183
{
1184
   assert(len > 0);
1,294,866✔
1185

1186
   const int nbytes = vsnprintf(buf, len, fmt, ap);
1,294,866✔
1187
   if (nbytes >= len)
1,294,866✔
1188
      fatal_trace("checked_sprintf requires %d bytes but have %d",
1189
                  nbytes + 1, len);
1190

1191
   return nbytes;
1,294,866✔
1192
}
1193

1194
int checked_sprintf(char *buf, int len, const char *fmt, ...)
1,294,852✔
1195
{
1196
   va_list ap;
1,294,852✔
1197
   va_start(ap, fmt);
1,294,852✔
1198

1199
   const int nbytes = checked_vsprintf(buf, len, fmt, ap);
1,294,852✔
1200

1201
   va_end(ap);
1,294,852✔
1202
   return nbytes;
1,294,852✔
1203
}
1204

1205
text_buf_t *tb_new(void)
484,591✔
1206
{
1207
   text_buf_t *tb = xmalloc(sizeof(text_buf_t));
484,591✔
1208
   tb->alloc = 256;
484,591✔
1209
   tb->len   = 0;
484,591✔
1210
   tb->buf   = xmalloc(tb->alloc);
484,591✔
1211

1212
   tb->buf[0] = '\0';
484,591✔
1213

1214
   return tb;
484,591✔
1215
}
1216

1217
void tb_free(text_buf_t *tb)
481,126✔
1218
{
1219
   if (tb != NULL) {
481,126✔
1220
      free(tb->buf);
481,126✔
1221
      free(tb);
481,126✔
1222
   }
1223
}
481,126✔
1224

1225
void _tb_cleanup(text_buf_t **tb)
566,883✔
1226
{
1227
   if (*tb != NULL)
566,883✔
1228
      tb_free(*tb);
383,698✔
1229
}
566,883✔
1230

1231
void tb_vprintf(text_buf_t *tb, const char *fmt, va_list ap)
413,766✔
1232
{
1233
   ostream_t os = { tb_ostream_write, tb, CHARSET_ISO88591 };
413,766✔
1234
   nvc_vfprintf(&os, fmt, ap);
413,766✔
1235
}
413,766✔
1236

1237
void tb_printf(text_buf_t *tb, const char *fmt, ...)
364,459✔
1238
{
1239
   va_list ap;
364,459✔
1240
   va_start(ap, fmt);
364,459✔
1241
   tb_vprintf(tb, fmt, ap);
364,459✔
1242
   va_end(ap);
364,459✔
1243
}
364,459✔
1244

1245
void tb_istr(text_buf_t *tb, ident_t ident)
412,428✔
1246
{
1247
   // TODO: this function seems useless now
1248
   tb_cat(tb, istr(ident));
412,428✔
1249
}
412,428✔
1250

1251
void tb_append(text_buf_t *tb, char ch)
1,752,870✔
1252
{
1253
   if (tb->len + 2 >= tb->alloc) {
1,752,870✔
1254
      tb->alloc *= 2;
197✔
1255
      tb->buf = xrealloc(tb->buf, tb->alloc);
197✔
1256
   }
1257

1258
   tb->buf[(tb->len)++] = ch;
1,752,870✔
1259
   tb->buf[tb->len] = '\0';
1,752,870✔
1260
}
1,752,870✔
1261

1262
void tb_catn(text_buf_t *tb, const char *str, size_t nchars)
2,928,662✔
1263
{
1264
   if (tb->len + nchars + 1 >= tb->alloc) {
2,928,662✔
1265
      tb->alloc = next_power_of_2(tb->alloc + nchars);
4,083✔
1266
      tb->buf = xrealloc(tb->buf, tb->alloc);
4,083✔
1267
   }
1268

1269
   memcpy(tb->buf + tb->len, str, nchars);
2,928,662✔
1270
   tb->len += nchars;
2,928,662✔
1271
   tb->buf[tb->len] = '\0';
2,928,662✔
1272
}
2,928,662✔
1273

1274
void tb_cat(text_buf_t *tb, const char *str)
1,149,728✔
1275
{
1276
   tb_catn(tb, str, strlen(str));
1,149,728✔
1277
}
1,149,728✔
1278

1279
void tb_repeat(text_buf_t *tb, char ch, size_t count)
×
1280
{
1281
   if (tb->len + count + 1 >= tb->alloc) {
×
UNCOV
1282
      tb->alloc = next_power_of_2(tb->alloc + count + 1);
×
UNCOV
1283
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1284
   }
1285

1286
   memset(tb->buf + tb->len, ch, count);
×
1287
   tb->len += count;
×
UNCOV
1288
   tb->buf[tb->len] = '\0';
×
1289
}
×
1290

1291
char *tb_reserve(text_buf_t *tb, size_t size)
×
1292
{
1293
   if (tb->len + size + 1 >= tb->alloc) {
×
UNCOV
1294
      tb->alloc = next_power_of_2(tb->alloc + size + 1);
×
UNCOV
1295
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1296
   }
1297

1298
   char *start = tb->buf + tb->len;
×
1299

UNCOV
1300
   tb->len += size;
×
1301
   tb->buf[tb->len] = '\0';
×
1302

UNCOV
1303
   return start;
×
1304
}
1305

1306
size_t tb_len(text_buf_t *tb)
145,110✔
1307
{
1308
   return tb->len;
145,110✔
1309
}
1310

1311
char *tb_claim(text_buf_t *tb)
47,234✔
1312
{
1313
   char *buf = tb->buf;
47,234✔
1314
   tb->buf = NULL;
47,234✔
1315
   return buf;
47,234✔
1316
}
1317

1318
const char *tb_get(text_buf_t *tb)
644,343✔
1319
{
1320
   return tb->buf;
644,343✔
1321
}
1322

1323
void tb_rewind(text_buf_t *tb)
84,669✔
1324
{
1325
   tb->len = 0;
84,669✔
1326
   tb->buf[0] = '\0';
84,669✔
1327
}
84,669✔
1328

1329
void tb_trim(text_buf_t *tb, size_t newlen)
1,169✔
1330
{
1331
   assert(newlen <= tb->len);
1,169✔
1332
   tb->len = newlen;
1,169✔
1333
   tb->buf[tb->len] = '\0';
1,169✔
1334
}
1,169✔
1335

1336
void tb_strip(text_buf_t *tb)
1✔
1337
{
1338
   while (tb->len > 0 && isspace_iso88591(tb->buf[tb->len - 1]))
7✔
1339
      tb->buf[--(tb->len)] = '\0';
6✔
1340
}
1✔
1341

1342
void tb_downcase(text_buf_t *tb)
145,715✔
1343
{
1344
   for (size_t i = 0; i < tb->len; i++)
25,548,411✔
1345
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
25,402,696✔
1346
}
145,715✔
1347

1348
void tb_upcase(text_buf_t *tb)
1,849✔
1349
{
1350
   for (size_t i = 0; i < tb->len; i++)
12,696✔
1351
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
10,847✔
1352
}
1,849✔
1353

1354
void tb_replace(text_buf_t *tb, char old, char rep)
686✔
1355
{
1356
   for (size_t i = 0; i < tb->len; i++) {
9,975✔
1357
      if (tb->buf[i] == old)
9,289✔
1358
         tb->buf[i] = rep;
686✔
1359
   }
1360
}
686✔
1361

1362
void tb_strftime(text_buf_t *tb, const char *fmt, time_t time)
×
1363
{
1364
   struct tm tm;
×
UNCOV
1365
   switch (fmt[0]) {
×
UNCOV
1366
   case 'L':
×
1367
#ifdef __MINGW32__
1368
      localtime_s(&tm, &time);
1369
#else
1370
      localtime_r(&time, &tm);
×
1371
#endif
UNCOV
1372
      break;
×
UNCOV
1373
   case 'G':
×
1374
#ifdef __MINGW32__
1375
      gmtime_s(&tm, &time);
1376
#else
1377
      gmtime_r(&time, &tm);
×
1378
#endif
UNCOV
1379
      break;
×
UNCOV
1380
   default:
×
1381
      fatal_trace("invalid timezone specifier '%c'", fmt[0]);
1382
   }
1383

1384
   const size_t max = 64;
×
UNCOV
1385
   char *p = tb_reserve(tb, max);
×
1386
   if (strftime(p, max, fmt + 1, &tm) == 0)
×
1387
      fatal_trace("time format buffer too small");
UNCOV
1388
}
×
1389

1390
void tb_ostream_write(const char *buf, size_t len, void *ctx)
1,298,961✔
1391
{
1392
   text_buf_t *tb = ctx;
1,298,961✔
1393
   tb_catn(tb, buf, len);
1,298,961✔
1394
}
1,298,961✔
1395

1396
void _local_free(void *ptr)
1,410,576✔
1397
{
1398
   free(*(void **)ptr);
1,410,576✔
1399
}
1,410,576✔
1400

1401
void set_message_style(message_style_t style)
×
1402
{
1403
   message_style = style;
×
1404

1405
   if (style == MESSAGE_COMPACT)
×
UNCOV
1406
      want_color = false;
×
UNCOV
1407
}
×
1408

1409
message_style_t get_message_style(void)
30,042✔
1410
{
1411
   return message_style;
30,042✔
1412
}
1413

1414
#ifndef __MINGW32__
1415
static uint64_t timeval_us(struct timeval *tv)
10,932✔
1416
{
1417
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
10,932✔
1418
}
1419
#endif
1420

1421
void nvc_rusage(nvc_rusage_t *ru)
5,466✔
1422
{
1423
#ifndef __MINGW32__
1424
   static uint64_t last_user, last_sys;
5,466✔
1425

1426
   struct rusage buf;
5,466✔
1427
   if (getrusage(RUSAGE_SELF, &buf) < 0)
5,466✔
UNCOV
1428
      fatal_errno("getrusage");
×
1429

1430
   const uint64_t user = timeval_us(&(buf.ru_utime));
5,466✔
1431
   const uint64_t sys = timeval_us(&(buf.ru_stime));
5,466✔
1432

1433
   ru->user = (user - last_user) / 1000;
5,466✔
1434
   ru->sys = (sys - last_sys) / 1000;
5,466✔
1435

1436
   last_sys = sys;
5,466✔
1437
   last_user = user;
5,466✔
1438

1439
#ifdef __APPLE__
1440
   const int rss_units = 1024;
1441
#else
1442
   const int rss_units = 1;
5,466✔
1443
#endif
1444

1445
   ru->rss = buf.ru_maxrss / rss_units;
5,466✔
1446
#else
1447
   static ULARGE_INTEGER last_kernel, last_user;
1448
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1449
   HANDLE hProcess = GetCurrentProcess();
1450

1451
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1452
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1453
      fatal_errno("GetProcessTimes");
1454

1455
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1456
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1457
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1458
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1459

1460
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1461
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1462

1463
   last_user = lv_Tuser;
1464
   last_kernel = lv_Tkernel;
1465

1466
   PROCESS_MEMORY_COUNTERS counters;
1467
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1468
      fatal_errno("GetProcessMemoryInfo");
1469

1470
   ru->rss = counters.PeakWorkingSetSize / 1024;
1471
#endif
1472

1473
   static uint64_t last_ts;
5,466✔
1474
   const uint64_t ts = get_timestamp_us();
5,466✔
1475
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
5,466✔
1476
   last_ts = ts;
5,466✔
1477
}
5,466✔
1478

1479
#ifdef __MINGW32__
1480
static uint64_t file_time_to_nanos(LPFILETIME ft)
1481
{
1482
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1483
   nanos |= ft->dwLowDateTime;
1484

1485
   // Windows file timestamps are in units of 100 nanoseconds since
1486
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1487
   // epoch 1970-01-01T00:00:00Z
1488
   nanos -= UINT64_C(116444736000000000);
1489
   nanos *= 100;
1490

1491
   return nanos;
1492
}
1493

1494
static bool fill_file_info(file_info_t *info, HANDLE handle)
1495
{
1496
   memset(info, '\0', sizeof(file_info_t));
1497

1498
   BY_HANDLE_FILE_INFORMATION hinfo;
1499
   if (!GetFileInformationByHandle(handle, &hinfo))
1500
      fatal_errno("GetFileInformationByHandle");
1501

1502
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1503
   info->size |= hinfo.nFileSizeLow;
1504

1505
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1506
      info->type = FILE_DIR;
1507
   else
1508
      info->type = FILE_REGULAR;
1509

1510
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1511

1512
   return true;
1513
}
1514
#else  // !__MINGW32__
1515
static void fill_file_info(file_info_t *info, const struct stat *st)
131,456✔
1516
{
1517
   memset(info, '\0', sizeof(file_info_t));
131,456✔
1518

1519
   info->size = st->st_size;
131,456✔
1520

1521
   if (S_ISDIR(st->st_mode))
131,456✔
1522
      info->type = FILE_DIR;
122✔
1523
   else if (!S_ISREG(st->st_mode))
131,334✔
1524
      info->type = FILE_FIFO;
72✔
1525
   else
1526
      info->type = FILE_REGULAR;
1527

1528
   info->mtime = st->st_mtime * UINT64_C(1000000000);
131,456✔
1529

1530
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1531
   info->mtime += st->st_mtimespec.tv_nsec;
1532
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1533
   info->mtime += st->st_mtim.tv_nsec;
131,456✔
1534
#endif
1535
}
131,456✔
1536
#endif  // !__MINGW32__
1537

1538
bool get_file_info(const char *path, file_info_t *info)
59,568✔
1539
{
1540
#ifdef __MINGW32__
1541
   HANDLE handle = CreateFile(
1542
        path, FILE_READ_ATTRIBUTES,
1543
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1544
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1545

1546
   if (handle == INVALID_HANDLE_VALUE)
1547
      return false;
1548

1549
   fill_file_info(info, handle);
1550

1551
   if (!CloseHandle(handle))
1552
      fatal_errno("CloseHandle");
1553

1554
   return true;
1555
#else
1556
   struct stat st;
59,568✔
1557
   if (stat(path, &st) == 0) {
59,568✔
1558
      fill_file_info(info, &st);
16,653✔
1559
      return true;
16,653✔
1560
   }
1561
   else
1562
      return false;
1563
#endif
1564
}
1565

1566
bool get_handle_info(int fd, file_info_t *info)
114,803✔
1567
{
1568
#ifdef __MINGW32__
1569
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1570
   fill_file_info(info, handle);
1571
   return true;
1572
#else
1573
   struct stat st;
114,803✔
1574
   if (fstat(fd, &st) == 0) {
114,803✔
1575
      fill_file_info(info, &st);
114,803✔
1576
      return true;
114,803✔
1577
   }
1578
   else
1579
      return false;
1580
#endif
1581
}
1582

UNCOV
1583
void run_program(const char *const *args)
×
1584
{
1585
#if defined __CYGWIN__ || defined __MINGW32__
1586
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1587
#else  // __CYGWIN__
1588
   pid_t pid = fork();
×
1589
   int status = 0;
×
1590
   if (pid == 0) {
×
UNCOV
1591
      execvp(args[0], (char *const *)args);
×
1592
      fatal_errno("execv");
×
1593
   }
1594
   else if (pid > 0) {
×
UNCOV
1595
      if (waitpid(pid, &status, 0) != pid)
×
1596
         fatal_errno("waitpid");
×
1597

UNCOV
1598
      status = WEXITSTATUS(status);
×
1599
   }
1600
   else
UNCOV
1601
      fatal_errno("fork");
×
1602
#endif  // __CYGWIN__
1603

1604
   if (status != 0) {
×
1605
      LOCAL_TEXT_BUF tb = tb_new();
×
1606
      for (size_t i = 0; args[i] != NULL; i++)
×
UNCOV
1607
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1608
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1609
   }
1610
}
×
1611

1612
char *nvc_temp_file(void)
×
1613
{
1614
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
×
1615
   const char *tmpdir = NULL;
×
UNCOV
1616
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
×
UNCOV
1617
      tmpdir = getenv(try[i]);
×
1618

1619
#ifdef __MINGW32__
1620
   char *buf = xasprintf("%s\\nvc-XXXXXX", tmpdir ?: ".");
1621
   return _mktemp(buf);
1622
#else
1623
   char *buf = xasprintf("%s/nvc-XXXXXX", tmpdir ?: "/tmp");
×
1624
   int fd = mkstemp(buf);
×
1625
   if (fd < 0)
×
1626
      fatal_errno("mkstemp");
×
UNCOV
1627
   close(fd);
×
UNCOV
1628
   return buf;
×
1629
#endif
1630
}
1631

1632
void file_read_lock(int fd)
53,691✔
1633
{
1634
#ifdef __MINGW32__
1635
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1636

1637
   LARGE_INTEGER li;
1638
   li.QuadPart = _filelengthi64(fd);
1639

1640
   OVERLAPPED ovlp;
1641
   memset(&ovlp, 0, sizeof ovlp);
1642

1643
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1644
      fatal_errno("LockFileEx");
1645
#else
1646
   if (flock(fd, LOCK_SH) < 0)
53,691✔
UNCOV
1647
      fatal_errno("flock");
×
1648
#endif
1649
}
53,691✔
1650

1651
void file_write_lock(int fd)
13,075✔
1652
{
1653
#ifdef __MINGW32__
1654
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1655

1656
   LARGE_INTEGER li;
1657
   li.QuadPart = _filelengthi64(fd);
1658

1659
   OVERLAPPED ovlp;
1660
   memset(&ovlp, 0, sizeof ovlp);
1661

1662
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1663
                   li.LowPart, li.HighPart, &ovlp))
1664
      fatal_errno("LockFileEx");
1665
#else
1666
   if (flock(fd, LOCK_EX) < 0)
13,075✔
UNCOV
1667
      fatal_errno("flock");
×
1668
#endif
1669
}
13,075✔
1670

1671
void file_unlock(int fd)
66,754✔
1672
{
1673
#ifdef __MINGW32__
1674
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1675

1676
   LARGE_INTEGER li;
1677
   li.QuadPart = _filelengthi64 (fd);
1678

1679
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1680
#else
1681
   if (flock(fd, LOCK_UN) < 0)
66,754✔
UNCOV
1682
      fatal_errno("flock");
×
1683
#endif
1684
}
66,754✔
1685

1686
void *map_file(int fd, size_t size)
61,094✔
1687
{
1688
#ifdef __MINGW32__
1689
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1690
                                     PAGE_READONLY, 0, size, NULL);
1691
   if (!handle)
1692
      fatal_errno("CreateFileMapping");
1693

1694
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1695
                               0, (SIZE_T) size, (LPVOID) NULL);
1696
   CloseHandle(handle);
1697
   if (ptr == NULL)
1698
      fatal_errno("MapViewOfFileEx");
1699
#else
1700
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
61,094✔
1701
   if (ptr == MAP_FAILED)
61,094✔
1702
      fatal_trace("mmap failed to map %zu byte file", size);
1703
#endif
1704
   return ptr;
61,094✔
1705
}
1706

1707
void unmap_file(void *ptr, size_t size)
53,982✔
1708
{
1709
#ifdef __MINGW32__
1710
   if (!UnmapViewOfFile((LPCVOID) ptr))
1711
      fatal_errno("UnmapViewOfFile");
1712
#else
1713
   munmap(ptr, size);
53,982✔
1714
#endif
1715
}
53,982✔
1716

1717
void make_dir(const char *fmt, ...)
6,190✔
1718
{
1719
   va_list ap;
6,190✔
1720
   va_start(ap, fmt);
6,190✔
1721
   char *path LOCAL = xvasprintf(fmt, ap);
12,380✔
1722
   va_end(ap);
6,190✔
1723

1724
#ifdef __MINGW32__
1725
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1726
      fatal_errno("mkdir: %s", path);
1727
#else
1728
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
6,190✔
UNCOV
1729
      fatal_errno("mkdir: %s", path);
×
1730
#endif
1731
}
6,190✔
1732

1733
FILE *create_file(const char *fmt, ...)
1,388✔
1734
{
1735
   va_list ap;
1,388✔
1736
   va_start(ap, fmt);
1,388✔
1737
   char *path LOCAL = xvasprintf(fmt, ap);
1,388✔
1738
   va_end(ap);
1,388✔
1739

1740
   FILE *f = fopen(path, "w");
1,388✔
1741
   if (f == NULL)
1,388✔
UNCOV
1742
      fatal_errno("cannot create %s", path);
×
1743

1744
   return f;
1,388✔
1745
}
1746

1747
uint64_t get_timestamp_ns(void)
67,084✔
1748
{
1749
#if defined __MINGW32__
1750
   static volatile uint64_t freq;
1751
   if (load_acquire(&freq) == 0) {
1752
      LARGE_INTEGER tmp;
1753
      if (!QueryPerformanceFrequency(&tmp))
1754
         fatal_errno("QueryPerformanceFrequency");
1755
      store_release(&freq, tmp.QuadPart);
1756
   }
1757

1758
   LARGE_INTEGER ticks;
1759
   if (!QueryPerformanceCounter(&ticks))
1760
      fatal_errno("QueryPerformanceCounter");
1761
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1762
#else
1763
   struct timespec ts;
67,084✔
1764
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
67,084✔
UNCOV
1765
      fatal_errno("clock_gettime");
×
1766
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
67,084✔
1767
#endif
1768
}
1769

1770
uint64_t get_timestamp_us(void)
67,084✔
1771
{
1772
   return get_timestamp_ns() / 1000;
67,084✔
1773
}
1774

1775
timestamp_t get_real_time(void)
23,257✔
1776
{
1777
#if defined __MINGW32__
1778
   FILETIME ft;
1779
   GetSystemTimeAsFileTime(&ft);
1780

1781
   return file_time_to_nanos(&ft);
1782
#else
1783
   struct timespec ts;
23,257✔
1784
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
23,257✔
UNCOV
1785
      fatal_errno("clock_gettime");
×
1786

1787
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
23,257✔
1788
#endif
1789
}
1790

1791
void open_pipe(int *rfd, int *wfd)
×
1792
{
UNCOV
1793
   int fds[2];
×
1794
#ifdef __MINGW32__
1795
   const int rc = _pipe(fds, 4096, _O_BINARY);
1796
#else
1797
   const int rc = pipe(fds) < 0;
×
1798
#endif
UNCOV
1799
   if (rc < 0)
×
1800
      fatal_errno("failed to create pipe");
1801

1802
   *rfd = fds[0];
×
UNCOV
1803
   *wfd = fds[1];
×
UNCOV
1804
}
×
1805

1806
#if defined _WIN32 || defined __CYGWIN__
1807
static struct {
1808
   char illegal;
1809
   const char *rep;
1810
} symbol_replacements[] = {
1811
   { '(', "_lp_"   },
1812
   { ')', "_rp_"   },
1813
   { '"', "_q_"    },
1814
   { '[', "_ls_"   },
1815
   { ']', "_rs_"   },
1816
   { '*', "_mult_" },
1817
   { '+', "_plus_" },
1818
   { '=', "_eq_"   },
1819
   { '\\', "_bs_"  },
1820
};
1821

1822
static text_buf_t *safe_symbol_win32(const char *text)
1823
{
1824
   text_buf_t *tb = tb_new();
1825

1826
   for (const char *p = text; *p != '\0'; p++) {
1827
      bool replaced = false;
1828
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
1829
         if (*p == symbol_replacements[j].illegal) {
1830
            tb_cat(tb, symbol_replacements[j].rep);
1831
            replaced = true;
1832
            break;
1833
         }
1834
      }
1835

1836
      if (!replaced)
1837
         tb_append(tb, *p);
1838
   }
1839

1840
   return tb;
1841
}
1842

1843
#endif
1844

UNCOV
1845
text_buf_t *safe_symbol(ident_t id)
×
1846
{
1847
   // Return a string that is safe to use as a symbol name on this platform
1848

UNCOV
1849
   text_buf_t *tb = tb_new();
×
UNCOV
1850
   tb_istr(tb, id);
×
1851

1852
#if defined _WIN32 || defined __CYGWIN__
1853
   if (strpbrk(tb_get(tb), "()\"[]*+=\\") == NULL)
1854
      return tb;
1855
   else {
1856
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
1857
      tb_free(tb);
1858
      return new;
1859
   }
1860
#else
UNCOV
1861
   return tb;
×
1862
#endif
1863
}
1864

1865
void __cleanup_array(void *ptr)
243,896✔
1866
{
1867
   A(void *) *a = ptr;
243,896✔
1868
   ACLEAR(*a);
243,896✔
1869
}
243,896✔
1870

1871
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
2,155,916✔
1872
                         size_t size)
1873
{
1874
   if (count == 0) {
2,155,916✔
1875
      free(*ptr);
×
UNCOV
1876
      *ptr = NULL;
×
UNCOV
1877
      *limit = 0;
×
1878
   }
1879
   else {
1880
      if (*limit == 0)
2,155,916✔
1881
         *limit = count;  // Setting the initial size of the array
1,882,958✔
1882
      else
1883
         *limit = next_power_of_2(count);
272,958✔
1884
      *ptr = xrealloc_array(*ptr, *limit, size);
2,155,916✔
1885
   }
1886
}
2,155,916✔
1887

1888
bool get_exe_path(text_buf_t *tb)
7,998✔
1889
{
1890
#if defined __linux__
1891
   char buf[PATH_MAX];
7,998✔
1892
   ssize_t nchars = readlink("/proc/self/exe", buf, sizeof(buf));
7,998✔
1893
   if (nchars > 0) {   // Does not append '\0'
7,998✔
1894
      tb_catn(tb, buf, nchars);
7,998✔
1895
      return true;
7,998✔
1896
   }
1897
#elif defined __APPLE__
1898
   char buf[PATH_MAX];
1899
   if (proc_pidpath(getpid(), buf, sizeof(buf)) > 0) {
1900
      tb_cat(tb, buf);
1901
      return true;
1902
   }
1903
#elif defined __FreeBSD__
1904
   char buf[PATH_MAX];
1905
   size_t size = sizeof(buf);
1906
   const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
1907
   if (sysctl(name, ARRAY_LEN(name), buf, &size, NULL, 0) == 0) {
1908
      tb_catn(tb, buf, size);
1909
      return true;
1910
   }
1911
#elif defined __MINGW32__
1912
   HANDLE hProc = GetCurrentProcess();
1913
   char buf[PATH_MAX];
1914
   DWORD size = sizeof(buf);
1915
   if (QueryFullProcessImageNameA(hProc, 0, buf, &size)) {
1916
      tb_cat(tb, buf);
1917
      return true;
1918
   }
1919
#endif
1920
   return false;
1921
}
1922

1923
#if defined __MINGW32__
1924
static void get_relative_prefix(text_buf_t *tb)
1925
{
1926
   if (get_exe_path(tb)) {
1927
      int len = tb_len(tb);
1928
      const char *str = tb_get(tb);
1929
      for (int i = 0; i < 2; i++) {
1930
         do {
1931
            len--;
1932
         } while (str[len] != DIR_SEP[0]);
1933
      }
1934
      tb_trim(tb, len);
1935
   }
1936
   else
1937
      fatal("failed to read executable path");
1938
}
1939
#endif
1940

UNCOV
1941
void get_libexec_dir(text_buf_t *tb)
×
1942
{
1943
#if defined __MINGW32__
1944
   get_relative_prefix(tb);
1945
   tb_cat(tb, DIR_SEP "libexec" DIR_SEP "nvc");
1946
#else
1947
   tb_cat(tb, LIBEXECDIR);
×
1948
#endif
UNCOV
1949
}
×
1950

1951
void get_lib_dir(text_buf_t *tb)
8,360✔
1952
{
1953
#if defined __MINGW32__
1954
   get_relative_prefix(tb);
1955
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
1956
#else
1957
   tb_cat(tb, LIBDIR);
8,360✔
1958
#endif
1959
}
8,360✔
1960

1961
void get_data_dir(text_buf_t *tb)
31✔
1962
{
1963
#if defined __MINGW32__
1964
   get_relative_prefix(tb);
1965
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
1966
#else
1967
   tb_cat(tb, DATADIR);
31✔
1968
#endif
1969
}
31✔
1970

1971
bool is_absolute_path(const char *path)
32✔
1972
{
1973
   if (path[0] == DIR_SEP[0] || path[0] == '/')
32✔
1974
      return true;
32✔
1975

1976
#ifdef __MINGW32__
1977
   if (isalpha((int)path[0]) && path[1] == ':')
1978
      return true;
1979
#endif
1980

1981
   return false;
1982
}
1983

1984
void get_relative_path(text_buf_t *tb, const char *from, const char *to)
291✔
1985
{
1986
#ifdef __MINGW32__
1987
    char abs_from[MAX_PATH], abs_to[MAX_PATH];
1988
    if (from == NULL || !_fullpath(abs_from, from, MAX_PATH))
1989
       goto fallback;
1990
    if (!_fullpath(abs_to, to, MAX_PATH))
1991
       goto fallback;
1992

1993
    char buffer[MAX_PATH];
1994
    if (!PathRelativePathToA(buffer, abs_from, FILE_ATTRIBUTE_DIRECTORY,
1995
                             abs_to, 0))
1996
       goto fallback;
1997

1998
    if (strncmp(buffer, ".\\", 2) == 0)
1999
       tb_cat(tb, buffer + 2);   // Strip leading .\ prefix
2000
    else
2001
       tb_cat(tb, buffer);
2002
#else
2003
   char abs_from[PATH_MAX], abs_to[PATH_MAX];
291✔
2004
   if (from == NULL || realpath(from, abs_from) == NULL)
542✔
2005
      goto fallback;
40✔
2006
   else if (realpath(to, abs_to) == NULL)
251✔
UNCOV
2007
      goto fallback;
×
2008

2009
   A(char *) from_parts = AINIT;
251✔
2010
   A(char *) to_parts = AINIT;
251✔
2011

2012
   for (char *tok = strtok(abs_from, "/"); tok; tok = strtok(NULL, "/"))
1,208✔
2013
      APUSH(from_parts, tok);
957✔
2014

2015
   for (char *tok = strtok(abs_to, "/"); tok; tok = strtok(NULL, "/"))
1,757✔
2016
      APUSH(to_parts, tok);
1,506✔
2017

2018
   int common = 0;
2019
   while (common < from_parts.count && common < to_parts.count
957✔
2020
          && strcmp(from_parts.items[common], to_parts.items[common]) == 0)
2,165✔
2021
      common++;
957✔
2022

2023
   for (int i = common; i < from_parts.count; i++) {
251✔
2024
      tb_cat(tb, "..");
×
UNCOV
2025
      if (i + 1 < from_parts.count || to_parts.count > common)
×
UNCOV
2026
         tb_cat(tb, "/");
×
2027
   }
2028

2029
   for (int i = common; i < to_parts.count; i++) {
800✔
2030
      tb_cat(tb, to_parts.items[i]);
549✔
2031
      if (i + 1 < to_parts.count)
549✔
2032
         tb_cat(tb, "/");
298✔
2033
   }
2034

2035
   ACLEAR(from_parts);
251✔
2036
   ACLEAR(to_parts);
251✔
2037
#endif
2038

2039
   if (tb_len(tb) == 0)
251✔
UNCOV
2040
      goto fallback;
×
2041

2042
   return;
251✔
2043

2044
 fallback:
40✔
2045
   tb_cat(tb, to);
40✔
2046
}
2047

2048
void get_hex_hash(const char *str, char out[SHA_HEX_LEN])
1,126✔
2049
{
2050
   SHA1_CTX ctx;
1,126✔
2051
   unsigned char hash[SHA1_LEN];
1,126✔
2052

2053
   SHA1Init(&ctx);
1,126✔
2054
   SHA1Update(&ctx, (const unsigned char *)str, strlen(str));
1,126✔
2055
   SHA1Final(hash, &ctx);
1,126✔
2056

2057
   for (int i = 0; i < SHA1_LEN; i++)
23,646✔
2058
      snprintf(out + i * 2, 3, "%02x", hash[i]);
22,520✔
2059
}
1,126✔
2060

2061
void progress(const char *fmt, ...)
19,223✔
2062
{
2063
   if (opt_get_int(OPT_VERBOSE)) {
19,223✔
UNCOV
2064
      va_list ap;
×
2065
      va_start(ap, fmt);
×
UNCOV
2066
      char *msg LOCAL = xvasprintf(fmt, ap);
×
2067
      va_end(ap);
×
2068

UNCOV
2069
      static nvc_rusage_t last_ru;
×
2070

UNCOV
2071
      nvc_rusage_t ru;
×
UNCOV
2072
      nvc_rusage(&ru);
×
2073

UNCOV
2074
      const double conc = (double)(ru.user + ru.sys) / ru.ms;
×
2075

UNCOV
2076
      if (!isinf(conc) && conc > 1.1)
×
UNCOV
2077
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
UNCOV
2078
               ru.rss - last_ru.rss);
×
2079
      else
UNCOV
2080
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
×
2081

UNCOV
2082
      last_ru = ru;
×
2083
   }
2084
}
19,223✔
2085

2086
unsigned nvc_nprocs(void)
8,894✔
2087
{
2088
#if defined _WIN32
2089
   SYSTEM_INFO sysinfo;
2090
   GetSystemInfo(&sysinfo);
2091

2092
   return sysinfo.dwNumberOfProcessors;
2093
#elif defined _SC_NPROCESSORS_ONLN
2094
   long count = sysconf(_SC_NPROCESSORS_ONLN);
8,894✔
2095
   if (count == -1)
8,894✔
UNCOV
2096
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2097

2098
#if defined __linux__ && defined HAVE_GETTID
2099
   // Restrict to the number of CPUs we are allowed to run on
2100
   cpu_set_t s;
8,894✔
2101
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
8,894✔
2102
      return MAX(1, MIN(count, CPU_COUNT(&s)));
8,894✔
2103
#endif
2104

UNCOV
2105
   return count;
×
2106
#else
2107
#warning Cannot detect number of processors on this platform
2108
   return 1;
2109
#endif
2110
}
2111

2112
void capture_registers(struct cpu_state *cpu)
879✔
2113
{
2114
#if defined HAVE_GETCONTEXT
2115
   ucontext_t uc;
879✔
2116
   if (getcontext(&uc) != 0)
879✔
UNCOV
2117
      fatal_errno("getcontext");
×
2118

2119
   fill_cpu_state(cpu, &uc);
879✔
2120
#elif defined __MINGW32__
2121
   CONTEXT context;
2122
   RtlCaptureContext(&context);
2123
   fill_cpu_state(cpu, &context);
2124
#elif defined HAVE_PTHREAD
2125
   assert(atomic_load(&thread_regs) == NULL);
2126
   atomic_store(&thread_regs, cpu);
2127

2128
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2129
      fatal_errno("pthread_kill");
2130

2131
   // Registers filled in by signal_handler
2132
   if (atomic_load(&thread_regs) != NULL)
2133
      fatal_trace("signal handler did not capture thread registers");
2134
#else
2135
#error cannot capture registers on this platform
2136
#endif
2137
}
879✔
2138

2139
void add_fault_handler(fault_fn_t fn, void *context)
14,131✔
2140
{
2141
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
14,131✔
2142
   h->next    = fault_handlers;
14,131✔
2143
   h->fn      = fn;
14,131✔
2144
   h->context = context;
14,131✔
2145

2146
   fault_handlers = h;
14,131✔
2147
}
14,131✔
2148

2149
void remove_fault_handler(fault_fn_t fn, void *context)
5,692✔
2150
{
2151
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
5,698✔
2152
      if ((*p)->fn == fn && (*p)->context == context) {
5,698✔
2153
         fault_handler_t *tmp = (*p)->next;
5,692✔
2154
         free(*p);
5,692✔
2155
         *p = tmp;
5,692✔
2156
         return;
5,692✔
2157
      }
2158
   }
2159

2160
   fatal_trace("no fault handler for %p with context %p", fn, context);
2161
}
2162

2163
void check_cpu_features(void)
7,774✔
2164
{
2165
#ifdef HAVE_POPCNT
2166
   if (!__builtin_cpu_supports("popcnt"))
7,774✔
UNCOV
2167
      fatal("CPU is missing support for POPCNT instruction");
×
2168
#endif
2169
}
7,774✔
2170

2171
#ifdef DEBUG
2172
void should_not_reach_here(void)
2173
{
2174
   fatal_trace("should not reach here");
2175
}
2176
#endif
2177

2178
mem_pool_t *pool_new(void)
42,809✔
2179
{
2180
   mem_pool_t *mp = xcalloc(sizeof(mem_pool_t));
42,809✔
2181
   mp->pageshift = POOL_PAGE_MIN;
42,809✔
2182

2183
   return mp;
42,809✔
2184
}
2185

2186
void pool_free(mem_pool_t *mp)
116,577✔
2187
{
2188
   if (mp == NULL)
116,577✔
2189
      return;
2190

2191
   for (pool_page_t *p = mp->pages, *tmp; p != NULL; p = tmp) {
71,136✔
2192
      tmp = p->next;
33,045✔
2193
      free(p);
33,045✔
2194
   }
2195

2196
   free(mp);
38,091✔
2197
}
2198

2199
static pool_page_t *pool_page_new(mem_pool_t *mp, size_t reqsz, size_t align)
36,517✔
2200
{
2201
   const size_t hdrsz = ALIGN_UP(sizeof(page_header_t), align);
36,517✔
2202
   const size_t minsz = next_power_of_2(reqsz + hdrsz);
36,517✔
2203
   const size_t allocsz = MAX(1 << mp->pageshift, minsz);
36,517✔
2204
   page_header_t *p = xmalloc(allocsz);
36,517✔
2205
   p->next  = mp->pages;
36,517✔
2206
   p->alloc = hdrsz;
36,517✔
2207
   p->size  = allocsz;
36,517✔
2208

2209
   ASAN_POISON(p + hdrsz, allocsz - hdrsz);
36,517✔
2210

2211
   if (mp->pageshift < POOL_PAGE_MAX)
36,517✔
2212
      mp->pageshift++;
36,517✔
2213

2214
   return (mp->pages = p);
36,517✔
2215
}
2216

2217
static void *pool_aligned_malloc(mem_pool_t *mp, size_t size, size_t align)
1,240,428✔
2218
{
2219
   assert(is_power_of_2(align));
1,240,428✔
2220

2221
   pool_page_t *page;
1,240,428✔
2222
   if (mp->pages == NULL)
1,240,428✔
2223
      page = pool_page_new(mp, size, align);
30,177✔
2224
   else {
2225
      const size_t base = ALIGN_UP(mp->pages->alloc + POOL_REDZONE, align);
1,210,251✔
2226
      if (base + size > mp->pages->size)
1,210,251✔
2227
         page = pool_page_new(mp, size, align);
6,340✔
2228
      else {
2229
         page = mp->pages;
1,203,911✔
2230
         page->alloc = base;
1,203,911✔
2231
      }
2232
   }
2233

2234
   assert((mp->pages->alloc & (align - 1)) == 0);
1,240,428✔
2235
   assert(mp->pages->alloc + size <= mp->pages->size);
1,240,428✔
2236
   assert(mp->pages->alloc >= sizeof(page_header_t));
1,240,428✔
2237

2238
   void *ptr = (void *)mp->pages + mp->pages->alloc;
1,240,428✔
2239
   mp->pages->alloc += size;
1,240,428✔
2240

2241
   ASAN_UNPOISON(ptr, size);
1,240,428✔
2242
   return ptr;
1,240,428✔
2243
}
2244

2245
void *pool_malloc(mem_pool_t *mp, size_t size)
994,629✔
2246
{
2247
   return pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
994,629✔
2248
}
2249

2250
void *pool_malloc_flex(mem_pool_t *mp, size_t fixed, size_t nelems,
938,962✔
2251
                       size_t size)
2252
{
2253
   size_t bytes;
938,962✔
2254
   if (__builtin_mul_overflow(nelems, size, &bytes))
938,962✔
2255
      fatal_trace("array size overflow: requested %zd * %zd bytes",
2256
                  nelems, size);
2257

2258
   return pool_malloc(mp, fixed + bytes);
938,962✔
2259
}
2260

2261
void *pool_malloc_array(mem_pool_t *mp, size_t nelems, size_t size)
18,785✔
2262
{
2263
   return pool_malloc_flex(mp, 0, nelems, size);
18,785✔
2264
}
2265

2266
void *pool_calloc(mem_pool_t *mp, size_t size)
245,799✔
2267
{
2268
   void *ptr = pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
245,799✔
2269
   memset(ptr, '\0', size);
245,799✔
2270
   return ptr;
245,799✔
2271
}
2272

2273
void pool_stats(mem_pool_t *mp, size_t *alloc, size_t *npages)
11,302✔
2274
{
2275
   *npages = 0;
11,302✔
2276
   *alloc = 0;
11,302✔
2277

2278
   for (pool_page_t *p = mp->pages; p != NULL; p = p->next) {
18,128✔
2279
      *npages += 1;
6,826✔
2280
      *alloc += p->alloc - sizeof(pool_page_t);
6,826✔
2281
   }
2282
}
11,302✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc