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

nickg / nvc / 27865744029

20 Jun 2026 08:32AM UTC coverage: 92.264% (-0.005%) from 92.269%
27865744029

push

github

nickg
Bump version to 1.21.1

78741 of 85343 relevant lines covered (92.26%)

642699.33 hits per line

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

69.45
/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,803,947✔
155
{
156
   void *p = malloc(size);
18,803,947✔
157
   if (p == NULL)
18,803,947✔
158
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
159
   return p;
18,803,947✔
160
}
161

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

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

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

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

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

192
   return xcalloc(fixed + bytes);
10,815,933✔
193
}
194

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

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

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

215
   return xrealloc(ptr, bytes);
3,586,896✔
216
}
217

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

225
   return xrealloc(ptr, bytes + fixed);
43,322✔
226
}
227

228
char *xstrdup(const char *str)
537,256✔
229
{
230
   char *copy = strdup(str);
537,256✔
231
   if (copy == NULL)
537,256✔
232
      fatal("memory exhausted (strdup)");
×
233
   return copy;
537,256✔
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✔
240
      fatal("memory exhausted (strndup)");
×
241
   return copy;
63✔
242
}
243

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

252
char *xasprintf(const char *fmt, ...)
220,111✔
253
{
254
   va_list ap;
220,111✔
255
   va_start(ap, fmt);
220,111✔
256
   char *strp = xvasprintf(fmt, ap);
220,111✔
257
   va_end(ap);
220,111✔
258
   return strp;
220,111✔
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,717✔
282
{
283
   diag_t *d = diag_new(DIAG_NOTE, NULL);
2,717✔
284
   va_list ap;
2,717✔
285
   va_start(ap, fmt);
2,717✔
286
   diag_vprintf(d, fmt, ap);
2,717✔
287
   va_end(ap);
2,717✔
288
   diag_emit(d);
2,717✔
289
}
2,717✔
290

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

301
bool color_terminal(void)
4,325✔
302
{
303
   return want_color;
4,325✔
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 {
316
      const int pad = (term_width - strlen(text)) / 2;
×
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✔
328
      _exit(status);
×
329
   else
330
      exit(status);
57✔
331
}
332

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

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

342
   diag_emit(d);
772✔
343
}
768✔
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
{
371
   diag_t *d = diag_new(DIAG_FATAL, loc);
×
372
   diag_suppress(d, false);
×
373

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

379
   diag_set_consumer(NULL, NULL);
×
380
   diag_emit(d);
×
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
{
401
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
402
   diag_suppress(d, false);
×
403

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

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

412
   show_stacktrace();
×
413

414
   show_bug_report();
×
415

416
   fatal_exit(EXIT_FAILURE);
×
417
}
418

419
void fatal_errno(const char *fmt, ...)
4✔
420
{
421
   int saved_errno = errno;
4✔
422

423
   diag_t *d = diag_new(DIAG_FATAL, NULL);
4✔
424
   diag_suppress(d, false);
4✔
425

426
   va_list ap;
4✔
427
   va_start(ap, fmt);
4✔
428
   diag_vprintf(d, fmt, ap);
4✔
429
   diag_printf(d, ": %s", strerror(saved_errno));
4✔
430
   va_end(ap);
4✔
431

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

437
void fatal_win32(const char *fmt, ...)
×
438
{
439
#ifdef __MINGW32__
440
   DWORD err = GetLastError();
441

442
   LPSTR mbuf = NULL;
443
   DWORD n = FormatMessageA(
444
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
445
      | FORMAT_MESSAGE_IGNORE_INSERTS,
446
      NULL, err,
447
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
448
      (LPSTR)&mbuf, 0, NULL);
449

450
   while (n > 0 && (mbuf[n - 1] == '\n' || mbuf[n - 1] == '\r'))
451
      mbuf[--n] = '\0';
452

453
   diag_t *d = diag_new(DIAG_FATAL, NULL);
454
   diag_suppress(d, false);
455

456
   va_list ap;
457
   va_start(ap, fmt);
458
   diag_vprintf(d, fmt, ap);
459
   diag_printf(d, ": %s", mbuf);
460
   va_end(ap);
461

462
   LocalFree(mbuf);
463

464
   diag_set_consumer(NULL, NULL);
465
   diag_emit(d);
466
   fatal_exit(EXIT_FAILURE);
467
#else
468
   should_not_reach_here();
469
#endif
470
}
471

472
static void trace_one_frame(uintptr_t pc, const char *module,
×
473
                            const char *srcfile, const char *symbol,
474
                            unsigned lineno, unsigned colno,
475
                            ptrdiff_t disp, frame_kind_t kind)
476
{
477
   ostream_t *os = nvc_stderr();
×
478

479
   nvc_fprintf(os, "[$green$%p$$] ", (void *)pc);
×
480
   if (kind == FRAME_LIB)
×
481
      nvc_fprintf(os, "($red$%s$$) ", module);
×
482
   if (srcfile != NULL)
×
483
      nvc_fprintf(os, "%s:%d ", srcfile, lineno);
×
484
   if (symbol != NULL) {
×
485
      nvc_fprintf(os, "$yellow$%s$$", symbol);
×
486
      if (srcfile == NULL && disp != 0)
×
487
         nvc_fprintf(os, "$yellow$+0x%"PRIxPTR"$$", disp);
×
488
   }
489
   if (kind == FRAME_VHDL)
×
490
      nvc_fprintf(os, " $magenta$[VHDL]$$");
×
491
   fprintf(stderr, "\n");
×
492

493
#ifndef __MINGW32__
494
   if (srcfile != NULL) {
×
495
      FILE *f = fopen(srcfile, "r");
×
496
      if (f != NULL) {
×
497
         char *line LOCAL = NULL;
×
498
         size_t linesz = 0;
×
499
         for (int i = 0, len; i < lineno + 1
×
500
                 && (len = getline(&line, &linesz, f)) != -1; i++) {
×
501
            if (i < lineno - 2)
×
502
               continue;
×
503

504
            if (len <= 1)
×
505
               continue;
×
506
            else if (line[len - 1] == '\n')
×
507
               line[len - 1] = '\0';
×
508

509
            if (i == lineno - 1)
×
510
               nvc_fprintf(os, "$cyan$$bold$-->$$ $cyan$%s$$\n", line);
×
511
            else
512
               nvc_fprintf(os, "    $cyan$%s$$\n", line);
×
513
         }
514
         fclose(f);
×
515
      }
516
   }
517
#endif
518
}
×
519

520
__attribute__((noinline))
521
void show_stacktrace(void)
×
522
{
523
   debug_info_t *di = debug_capture();
×
524

525
   const int nframes = debug_count_frames(di);
×
526
   for (int n = 1; n < nframes; n++) {
×
527
      const debug_frame_t *f = debug_get_frame(di, n);
×
528

529
      for (debug_inline_t *inl = f->inlined; inl != NULL; inl = inl->next)
×
530
         trace_one_frame(f->pc, f->module, inl->srcfile, inl->symbol,
×
531
                         inl->lineno, inl->colno, f->disp, f->kind);
×
532

533
      trace_one_frame(f->pc, f->module, f->srcfile, f->symbol, f->lineno,
×
534
                      f->colno, f->disp, f->kind);
×
535

536
   }
537

538
   debug_free(di);
×
539

540
#if defined __linux__ && !defined HAVE_LIBDW && !defined HAVE_LIBDWARF
541
   nvc_fprintf(nvc_stderr(), "\n$cyan$Hint: you can get better stack traces by "
542
               "installing the libdw-dev package and reconfiguring$$\n");
543
#endif
544

545
   fflush(stderr);
×
546
}
×
547

548
static void show_bug_report(void)
549
{
550
#ifndef DEBUG
551
   extern const char version_string[];
552
   nvc_fprintf(nvc_stderr(), "\n$!red$%s ["TARGET_SYSTEM"]\n\n"
553
               "Please report this bug at "PACKAGE_BUGREPORT"$$\n\n",
554
               version_string);
555
   fflush(stderr);
556
#endif
557
}
558

559
#ifdef __MINGW32__
560

561
static const char *exception_name(DWORD code)
562
{
563
   switch (code) {
564
   case EXCEPTION_ACCESS_VIOLATION:
565
      return "EXCEPTION_ACCESS_VIOLATION";
566
   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
567
      return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
568
   case EXCEPTION_BREAKPOINT:
569
      return "EXCEPTION_BREAKPOINT";
570
   case EXCEPTION_DATATYPE_MISALIGNMENT:
571
      return "EXCEPTION_DATATYPE_MISALIGNMENT";
572
   case EXCEPTION_ILLEGAL_INSTRUCTION:
573
      return "EXCEPTION_ILLEGAL_INSTRUCTION";
574
   case EXCEPTION_IN_PAGE_ERROR:
575
      return "EXCEPTION_IN_PAGE_ERROR";
576
   case EXCEPTION_INT_DIVIDE_BY_ZERO:
577
      return "EXCEPTION_INT_DIVIDE_BY_ZERO";
578
   case EXCEPTION_INT_OVERFLOW:
579
      return "EXCEPTION_INT_OVERFLOW";
580
   case EXCEPTION_PRIV_INSTRUCTION:
581
      return "EXCEPTION_PRIV_INSTRUCTION";
582
   case EXCEPTION_STACK_OVERFLOW:
583
      return "EXCEPTION_STACK_OVERFLOW";
584
   }
585

586
   return "???";
587
}
588

589
WINAPI
590
static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
591
{
592
   DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
593
   PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
594

595
#if defined ARCH_ARM64
596
   DWORD64 ip = ExceptionInfo->ContextRecord->Pc;
597
#elif defined ARCH_X86_64
598
   DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
599
#else
600
   DWORD ip = ExceptionInfo->ContextRecord->Eip;
601
#endif
602

603
   if (code == EXCEPTION_ACCESS_VIOLATION)
604
      addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
605

606
   fprintf(stderr, "\n%s*** Caught exception %x (%s)",
607
           want_color ? "\033[31m\033[1m" : "",
608
           (int)code, exception_name(code));
609

610
   switch (code) {
611
   case EXCEPTION_ACCESS_VIOLATION:
612
   case EXCEPTION_ILLEGAL_INSTRUCTION:
613
      fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
614
      break;
615
   }
616

617
   fprintf(stderr, " ***%s\n\n", want_color ? "\033[0m" : "");
618
   fflush(stderr);
619

620
#ifdef __WIN64
621
   if (code != EXCEPTION_STACK_OVERFLOW)
622
      show_stacktrace();
623
#endif
624

625
  return EXCEPTION_EXECUTE_HANDLER;
626
}
627

628
#else
629

630
#ifndef __SANITIZE_THREAD__
631
static const char *signame(int sig, siginfo_t *info)
×
632
{
633
   switch (sig) {
×
634
   case SIGSEGV:
×
635
#ifdef __linux__
636
      switch (info->si_code) {
×
637
      case SEGV_MAPERR: return "SEGV_MAPERR";
638
      case SEGV_ACCERR: return "SEGV_ACCERR";
×
639
      default: return "SIGSEGV";
×
640
      }
641
#else
642
      return "SIGSEGV";
643
#endif
644
   case SIGABRT: return "SIGABRT";
645
   case SIGILL: return "SIGILL";
×
646
   case SIGFPE: return "SIGFPE";
×
647
   case SIGUSR1: return "SIGUSR1";
×
648
   case SIGUSR2: return "SIGUSR2";
×
649
   case SIGBUS: return "SIGBUS";
×
650
   case SIGINT: return "SIGINT";
×
651
   case SIGTRAP: return "SIGTRAP";
×
652
   default: return "???";
×
653
   }
654
}
655

656
static void print_fatal_signal(int sig, siginfo_t *info, struct cpu_state *cpu)
×
657
{
658
   static volatile __thread sig_atomic_t recurse = 0;
×
659

660
   if (recurse++ > 1) {
×
661
      signal(SIGABRT, SIG_DFL);
×
662
      abort();
×
663
   }
664

665
   char buf[512], *p = buf, *s = buf, *end = buf + ARRAY_LEN(buf);
×
666
   p += checked_sprintf(p, end - p, "\n%s*** Caught signal %d (%s)%s",
×
667
                        want_color ? "\033[31m\033[1m" : "",
×
668
                        sig, signame(sig, info),
669
                        recurse > 1 ? " inside signal handler" : "");
×
670

671
   switch (sig) {
×
672
   case SIGSEGV:
×
673
   case SIGILL:
674
   case SIGFPE:
675
   case SIGBUS:
676
      p += checked_sprintf(p, end - p, " [address=%p, ip=%p]",
×
677
                           info->si_addr, (void*)cpu->pc);
×
678
      break;
×
679
   }
680

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

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

685
   if (sig != SIGUSR1 && !atomic_cas(&crashing, SIG_ATOMIC_MAX, thread_id())) {
×
686
      sleep(60);
×
687
      _exit(EXIT_FAILURE);
×
688
   }
689
}
×
690
#endif  // !__SANITIZE_THREAD__
691

692
static __thread struct cpu_state *thread_regs = NULL;
693

694
static void signal_handler(int sig, siginfo_t *info, void *context)
×
695
{
696
   ucontext_t *uc = (ucontext_t*)context;
×
697
   struct cpu_state cpu;
×
698
   fill_cpu_state(&cpu, uc);
×
699

700
   struct cpu_state *req;
×
701
   if (sig == SIGUSR2 && (req = atomic_load(&thread_regs)) != NULL) {
×
702
      // Fill in registers for capture_registers
703
      *req = cpu;
×
704
      atomic_store(&thread_regs, NULL);
×
705
      return;
×
706
   }
707
   else if (sig == SIGINT) {
×
708
      void (*fn)(void *) = atomic_load(&ctrl_c_fn);
×
709
      if (fn != NULL) {
×
710
         (*fn)(ctrl_c_arg);
×
711
         return;
×
712
      }
713
   }
714

715
#ifdef __SANITIZE_THREAD__
716
   abort();
717
#else
718

719
   for (fault_handler_t *f = fault_handlers; f; f = f->next)
×
720
      (*f->fn)(sig, info->si_addr, &cpu, f->context);
×
721

722
   print_fatal_signal(sig, info, &cpu);
×
723

724
   show_stacktrace();
×
725

726
   show_bug_report();
×
727

728
   if (sig != SIGUSR1)
×
729
      _exit(2);
×
730
#endif  // !__SANITIZE_THREAD__
731
}
732
#endif  // ! __MINGW32__
733

734
void register_signal_handlers(void)
9,104✔
735
{
736
#ifdef __MINGW32__
737
   SetUnhandledExceptionFilter(win32_exception_handler);
738
#else
739

740
   struct sigaction sa = {
9,104✔
741
      .sa_sigaction = signal_handler,
742
      .sa_flags = SA_RESTART | SA_SIGINFO
743
   };
744
   sigemptyset(&sa.sa_mask);
9,104✔
745

746
#ifndef __SANITIZE_THREAD__
747
   sigaction(SIGSEGV, &sa, NULL);
9,104✔
748
   sigaction(SIGUSR1, &sa, NULL);
9,104✔
749
   sigaction(SIGFPE, &sa, NULL);
9,104✔
750
   sigaction(SIGBUS, &sa, NULL);
9,104✔
751
   sigaction(SIGILL, &sa, NULL);
9,104✔
752
   sigaction(SIGABRT, &sa, NULL);
9,104✔
753
   sigaction(SIGTRAP, &sa, NULL);
9,104✔
754
#endif  // !__SANITIZE_THREAD__
755
   sigaction(SIGUSR2, &sa, NULL);
9,104✔
756
#endif  // !__MINGW32__
757
}
9,104✔
758

759
#ifdef __MINGW32__
760
static BOOL win32_ctrl_c_handler(DWORD fdwCtrlType)
761
{
762
   switch (fdwCtrlType) {
763
   case CTRL_C_EVENT:
764
      {
765
         void (*fn)(void *) = atomic_load(&ctrl_c_fn);
766
         if (fn != NULL)
767
            (*fn)(ctrl_c_arg);
768
         return TRUE;
769
      }
770

771
   default:
772
      return FALSE;
773
   }
774
}
775
#endif
776

777
void set_ctrl_c_handler(void (*fn)(void *), void *arg)
11,152✔
778
{
779
   ctrl_c_arg = arg;
11,152✔
780
   atomic_store(&ctrl_c_fn, fn);
11,152✔
781

782
   if (fn != NULL) {
11,152✔
783
#ifndef __MINGW32__
784
      struct sigaction sa = {};
5,576✔
785
      sa.sa_sigaction = signal_handler;
5,576✔
786
      sigemptyset(&sa.sa_mask);
5,576✔
787
      sa.sa_flags = SA_RESTART | SA_SIGINFO;
5,576✔
788

789
      sigaction(SIGINT, &sa, NULL);
5,576✔
790
#else
791
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, TRUE))
792
         fatal_trace("SetConsoleCtrlHandler");
793
#endif
794
   }
795
   else {
796
#ifndef __MINGW32__
797
      struct sigaction sa = {};
5,576✔
798
      sa.sa_handler = SIG_DFL;
5,576✔
799
      sigaction(SIGINT, &sa, NULL);
5,576✔
800
#else
801
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
802
         fatal_trace("SetConsoleCtrlHandler");
803
#endif
804
   }
805
}
11,152✔
806

807
#ifdef __MINGW32__
808
static void restore_win32_codepage(void)
809
{
810
   assert(win32_codepage != 0);
811
   SetConsoleOutputCP(win32_codepage);
812
}
813
#endif
814

815
void term_init(void)
9,104✔
816
{
817
   const char *nvc_colors = getenv("NVC_COLORS");
9,104✔
818
   const char *term = getenv("TERM") ?: "";
9,104✔
819

820
   static const char *term_blacklist[] = {
9,104✔
821
      "dumb"
822
   };
823

824
   spin_wait();  // Dummy, to force linking thread.c
9,104✔
825

826
   bool is_tty = isatty(STDERR_FILENO);
9,104✔
827

828
#ifdef __MINGW32__
829
   if (!is_tty) {
830
      // Handle running under MinTty
831
      HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
832
      const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
833
      FILE_NAME_INFO *nameinfo = malloc(size);
834
      if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
835
         fatal_win32("GetFileInformationByHandle");
836

837
      if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
838
           || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
839
          && wcsstr(nameinfo->FileName, L"pty") != NULL)
840
         is_tty = true;
841

842
      free(nameinfo);
843
   }
844
#endif
845

846
   if (nvc_colors && strcmp(nvc_colors, "always") == 0)
9,104✔
847
      want_color = true;
4✔
848
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
9,100✔
849
      want_color = false;
×
850
   else {
851
      want_color = is_tty;
9,100✔
852

853
      if (want_color && (term != NULL)) {
9,100✔
854
         for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
×
855
            if (strcmp(term, term_blacklist[i]) == 0) {
×
856
               want_color = false;
×
857
               break;
×
858
            }
859
         }
860
      }
861
   }
862

863
#ifdef __MINGW32__
864
   HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
865
   DWORD mode;
866
   if (GetConsoleMode(hConsole, &mode)) {
867
      mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
868
      if (!SetConsoleMode(hConsole, mode))
869
         want_color = false;
870

871
      CONSOLE_SCREEN_BUFFER_INFO info;
872
      if (GetConsoleScreenBufferInfo(hConsole, &info))
873
         term_width = info.dwSize.X;
874
      else
875
         term_width = 80;
876
   }
877
#else
878
   if (is_tty) {
9,104✔
879
      // Try to find the terminal size with tcgetwinsize or TIOCGWINSZ
880
      term_width = 80;
×
881
#if defined HAVE_TCGETWINSIZE
882
      struct winsize ws;
883
      if (tcgetwinsize(STDIN_FILENO, &ws) == 0)
884
         term_width = ws.ws_col;
885
#elif defined TIOCGWINSZ
886
      struct winsize ws;
×
887
      if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
×
888
         term_width = ws.ws_col;
×
889
#endif
890
   }
891
#endif
892

893
#ifndef __MINGW32__
894
   // Assume the terminal is expecting UTF-8 by default
895
   want_utf8 = true;
9,104✔
896

897
   const char *lang = getenv("LANG");
9,104✔
898
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
9,104✔
899
      want_utf8 = false;
×
900
#else
901
   win32_codepage = GetConsoleOutputCP();
902
   if (win32_codepage == 65001)
903
      want_utf8 = true;
904
   else if (win32_codepage != 28591) {
905
      SetConsoleOutputCP(28591);
906
      atexit(restore_win32_codepage);
907
   }
908
#endif
909

910
   // Diagnostics are printed to stderr and explicitly flushed
911
   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
9,104✔
912
}
9,104✔
913

914
int terminal_width(void)
31,344✔
915
{
916
   return term_width;
31,344✔
917
}
918

919
const char *ordinal_str(int n)
30✔
920
{
921
   switch (n) {
30✔
922
   case 1: return "first";
923
   case 2: return "second";
6✔
924
   case 3: return "third";
×
925
   default:
×
926
      {
927
         static char buf[16];
×
928
         if (n > 20 && n % 10 == 1)
×
929
            checked_sprintf(buf, sizeof(buf), "%dst", n);
×
930
         else if (n > 20 && n % 10 == 2)
×
931
            checked_sprintf(buf, sizeof(buf), "%dnd", n);
×
932
         else if (n > 20 && n % 10 == 2)
×
933
            checked_sprintf(buf, sizeof(buf), "%drd", n);
×
934
         else
935
            checked_sprintf(buf, sizeof(buf), "%dth", n);
×
936
         return buf;
937
      }
938
   }
939
}
940

941
char *null_terminate(const uint8_t *data, size_t len)
1,735✔
942
{
943
   char *cstr = xmalloc(len + 1);
1,735✔
944
   if (data != NULL)
1,735✔
945
      memcpy(cstr, data, len);
1,735✔
946
   else
947
      assert(len == 0);
×
948
   cstr[len] = '\0';
1,735✔
949
   return cstr;
1,735✔
950
}
951

952
char toupper_iso88591(unsigned char ch)
4,635,156✔
953
{
954
   if (ch >= 'a' && ch <= 'z')
4,635,156✔
955
      return ch - 'a' + 'A';
2,441,289✔
956
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
2,193,867✔
957
      return ch - 0x20;
39✔
958
   else
959
      return ch;
2,193,828✔
960
}
961

962
char tolower_iso88591(unsigned char ch)
25,473,136✔
963
{
964
   if (ch >= 'A' && ch <= 'Z')
25,473,136✔
965
      return ch + 'a' - 'A';
1,214,725✔
966
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
24,258,411✔
967
      return ch + 0x20;
240✔
968
   else
969
      return ch;
24,258,171✔
970
}
971

972
bool isprint_iso88591(unsigned char ch)
395,575✔
973
{
974
   return (ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff);
395,575✔
975
}
976

977
bool isspace_iso88591(unsigned char ch)
246,063✔
978
{
979
   return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f'
246,063✔
980
      || ch == '\r' || ch == 0xa0;
246,063✔
981
}
982

983
bool isdigit_iso88591(unsigned char ch)
39,026✔
984
{
985
   return ch >= '0' && ch <= '9';
39,026✔
986
}
987

988
bool isupper_iso88591(unsigned char ch)
3,190✔
989
{
990
   return (ch >= 'A' && ch <= 'Z')
3,190✔
991
      || (ch >= 0xc0 && ch <= 0xd6)
3,190✔
992
      || (ch >= 0xd8 && ch <= 0xde);
3,190✔
993
}
994

995
bool isalpha_iso88591(unsigned char ch)
2,095,720✔
996
{
997
   return (ch >= 'A' && ch <= 'Z')
2,095,720✔
998
      || (ch >= 'a' && ch <= 'z')
2,095,720✔
999
      || (ch >= 0xc0 && ch <= 0xd6)
1000
      || (ch >= 0xd8 && ch <= 0xde)
1001
      || (ch >= 0xe0 && ch <= 0xf6)
1002
      || (ch >= 0xf8 && ch <= 0xfe);
2,095,720✔
1003
}
1004

1005
bool isalnum_iso88591(unsigned char ch)
516,146✔
1006
{
1007
   return isalpha_iso88591(ch) || (ch >= '0' && ch <= '9');
516,146✔
1008
}
1009

1010
int next_power_of_2(int n)
1,723,538✔
1011
{
1012
   n--;
1,723,538✔
1013
   n |= n >> 1;
1,723,538✔
1014
   n |= n >> 2;
1,723,538✔
1015
   n |= n >> 4;
1,723,538✔
1016
   n |= n >> 8;
1,723,538✔
1017
   n |= n >> 16;
1,723,538✔
1018
   n++;
1,723,538✔
1019
   return n;
1,723,538✔
1020
}
1021

1022
int ilog2(int64_t n)
22,533✔
1023
{
1024
   if (n <= 1)
22,533✔
1025
      return 0;
1026
   else {
1027
      int r = 0;
1028
      int64_t c = 1;
1029
      while (c < n) {
98,838✔
1030
         r += 1;
76,395✔
1031
         c <<= 1;
76,395✔
1032
      }
1033
      return r;
22,443✔
1034
   }
1035
}
1036

1037
bool ipow_safe(int64_t x, int64_t y, int64_t *result)
415✔
1038
{
1039
   assert(y >= 0);
415✔
1040
   int overflow = 0, xo = 0;
1041
   int64_t r = 1;
1042
   while (y) {
1,558✔
1043
      if (y & 1)
1,143✔
1044
         overflow |= xo || __builtin_mul_overflow(r, x, &r);
653✔
1045
      y >>= 1;
1,143✔
1046
      xo |= __builtin_mul_overflow(x, x, &x);
1,143✔
1047
   }
1048
   *result = r;
415✔
1049
   return !overflow;
415✔
1050
}
1051

1052
int64_t ipow(int64_t x, int64_t y)
2✔
1053
{
1054
   int64_t result;
2✔
1055
   if (!ipow_safe(x, y, &result))
2✔
1056
      DEBUG_ONLY(fatal_trace("integer overflow in ipow"));
1057

1058
   return result;
2✔
1059
}
1060

1061
#if !ASAN_ENABLED
1062
static long nvc_page_size(void)
1063
{
1064
#ifdef __MINGW32__
1065
   SYSTEM_INFO si;
1066
   GetSystemInfo(&si);
1067
   return si.dwPageSize;
1068
#else
1069
   return sysconf(_SC_PAGESIZE);
1070
#endif
1071
}
1072
#endif
1073

1074
void nvc_munmap(void *ptr, size_t length)
27,235✔
1075
{
1076
#if ASAN_ENABLED
1077
   free(ptr);
27,235✔
1078
#elif !defined __MINGW32__
1079
   if (munmap(ptr, length) != 0)
1080
      fatal_errno("munmap");
1081
#else
1082
   if (!VirtualFree(ptr, length, MEM_DECOMMIT))
1083
      fatal_win32("VirtualFree");
1084
#endif
1085
}
27,235✔
1086

1087
void *nvc_memalign(size_t align, size_t sz)
91,361✔
1088
{
1089
#if ASAN_ENABLED
1090
   void *ptr;
91,361✔
1091
   if (posix_memalign(&ptr, align, sz) != 0)
182,722✔
1092
      fatal_errno("posix_memalign");
×
1093

1094
   memset(ptr, '\0', sz);
91,361✔
1095
   return ptr;
91,361✔
1096
#else
1097
   assert(is_power_of_2(align));
1098
   const size_t mapalign = MAX(align, nvc_page_size());
1099
   const size_t mapsz = ALIGN_UP(sz + mapalign - 1, mapalign);
1100

1101
#if defined __MINGW32__
1102
   void *ptr = VirtualAlloc(NULL, mapsz, MEM_COMMIT | MEM_RESERVE,
1103
                            PAGE_READWRITE);
1104
   if (ptr == NULL)
1105
      fatal_win32("VirtualAlloc");
1106
#else
1107
   void *ptr = mmap(NULL, mapsz, PROT_READ | PROT_WRITE,
1108
                    MAP_PRIVATE | MAP_ANON, -1, 0);
1109
   if (ptr == MAP_FAILED)
1110
      fatal_errno("mmap failed to allocate %zu bytes", sz);
1111
#endif
1112

1113
   void *aligned = ALIGN_UP(ptr, align);
1114
   void *limit = aligned + sz;
1115

1116
   if (align > nvc_page_size()) {
1117
      const size_t low_waste = aligned - ptr;
1118
      const size_t high_waste = ptr + mapsz - limit;
1119
      assert(low_waste + high_waste == align);
1120

1121
      if (low_waste > 0) nvc_munmap(ptr, low_waste);
1122
      if (high_waste > 0) nvc_munmap(limit, high_waste);
1123
   }
1124

1125
   return aligned;
1126
#endif
1127
}
1128

1129
void nvc_memprotect(void *ptr, size_t length, mem_access_t prot)
92,829✔
1130
{
1131
#if defined __MINGW32__
1132
   static const int map[] = {
1133
      PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ,
1134
      PAGE_EXECUTE_READWRITE
1135
   };
1136
   DWORD old_prot;
1137
   if (length > 0 && !VirtualProtect(ptr, length, map[prot], &old_prot))
1138
      fatal_win32("VirtualProtect");
1139
#else
1140
#if ASAN_ENABLED
1141
   // LeakSanitizer will not detect leaks in regions mapped read-only
1142
   if (prot == MEM_RO || prot == MEM_NONE)
92,829✔
1143
      return;
1144
#endif
1145
   static const int map[] = {
5,859✔
1146
      PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
1147
      PROT_READ | PROT_WRITE | PROT_EXEC
1148
   };
1149
   if (mprotect(ptr, length, map[prot]) < 0)
5,859✔
1150
      fatal_errno("mprotect");
×
1151
#endif
1152
}
1153

1154
void nvc_decommit(void *ptr, size_t length)
23,560✔
1155
{
1156
#if defined __MINGW32__
1157
   if (length > 0 && !VirtualFree(ptr, length, MEM_DECOMMIT))
1158
      fatal_win32("VirtualFree");
1159
#elif defined __linux__
1160
   if (madvise(ptr, length, MADV_DONTNEED) != 0)
23,560✔
1161
      fatal_errno("madvise: MADV_DONTNEED");
×
1162
#endif
1163
}
23,560✔
1164

1165
void *map_huge_pages(size_t align, size_t sz)
27,275✔
1166
{
1167
#ifdef __linux__
1168
   if (sz >= HUGE_PAGE_SIZE) {
27,275✔
1169
      const size_t mapsz = ALIGN_UP(sz, HUGE_PAGE_SIZE);
26,697✔
1170
      void *mem = nvc_memalign(MAX(HUGE_PAGE_SIZE, align), mapsz);
26,697✔
1171

1172
      if (madvise(mem, mapsz, MADV_HUGEPAGE) < 0)
26,697✔
1173
         warnf("madvise: MADV_HUGEPAGE: %s", strerror(errno));
×
1174

1175
      return mem;
26,697✔
1176
   }
1177
#endif
1178

1179
   return nvc_memalign(align, sz);
578✔
1180
}
1181

1182
void *map_jit_pages(size_t align, size_t sz)
5,859✔
1183
{
1184
#ifdef __APPLE__
1185
   void *ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
1186
                    MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
1187
   if (ptr == MAP_FAILED)
1188
      fatal_errno("mmap failed to allocate %zu bytes for executable code", sz);
1189
#else
1190
   void *ptr = map_huge_pages(align, sz);
5,859✔
1191
   nvc_memprotect(ptr, sz, MEM_RWX);
5,859✔
1192
#endif
1193

1194
   return ptr;
5,859✔
1195
}
1196

1197
int checked_vsprintf(char *buf, int len, const char *fmt, va_list ap)
1,318,619✔
1198
{
1199
   assert(len > 0);
1,318,619✔
1200

1201
   const int nbytes = vsnprintf(buf, len, fmt, ap);
1,318,619✔
1202
   if (nbytes >= len)
1,318,619✔
1203
      fatal_trace("checked_sprintf requires %d bytes but have %d",
1204
                  nbytes + 1, len);
1205

1206
   return nbytes;
1,318,619✔
1207
}
1208

1209
int checked_sprintf(char *buf, int len, const char *fmt, ...)
1,318,605✔
1210
{
1211
   va_list ap;
1,318,605✔
1212
   va_start(ap, fmt);
1,318,605✔
1213

1214
   const int nbytes = checked_vsprintf(buf, len, fmt, ap);
1,318,605✔
1215

1216
   va_end(ap);
1,318,605✔
1217
   return nbytes;
1,318,605✔
1218
}
1219

1220
text_buf_t *tb_new(void)
496,192✔
1221
{
1222
   text_buf_t *tb = xmalloc(sizeof(text_buf_t));
496,192✔
1223
   tb->alloc = 256;
496,192✔
1224
   tb->len   = 0;
496,192✔
1225
   tb->buf   = xmalloc(tb->alloc);
496,192✔
1226

1227
   tb->buf[0] = '\0';
496,192✔
1228

1229
   return tb;
496,192✔
1230
}
1231

1232
void tb_free(text_buf_t *tb)
492,673✔
1233
{
1234
   if (tb != NULL) {
492,673✔
1235
      free(tb->buf);
492,673✔
1236
      free(tb);
492,673✔
1237
   }
1238
}
492,673✔
1239

1240
void _tb_cleanup(text_buf_t **tb)
575,793✔
1241
{
1242
   if (*tb != NULL)
575,793✔
1243
      tb_free(*tb);
392,457✔
1244
}
575,793✔
1245

1246
void tb_vprintf(text_buf_t *tb, const char *fmt, va_list ap)
422,970✔
1247
{
1248
   ostream_t os = { tb_ostream_write, tb, CHARSET_ISO88591 };
422,970✔
1249
   nvc_vfprintf(&os, fmt, ap);
422,970✔
1250
}
422,970✔
1251

1252
void tb_printf(text_buf_t *tb, const char *fmt, ...)
369,257✔
1253
{
1254
   va_list ap;
369,257✔
1255
   va_start(ap, fmt);
369,257✔
1256
   tb_vprintf(tb, fmt, ap);
369,257✔
1257
   va_end(ap);
369,257✔
1258
}
369,257✔
1259

1260
void tb_istr(text_buf_t *tb, ident_t ident)
414,602✔
1261
{
1262
   // TODO: this function seems useless now
1263
   tb_cat(tb, istr(ident));
414,602✔
1264
}
414,602✔
1265

1266
void tb_append(text_buf_t *tb, char ch)
1,806,154✔
1267
{
1268
   if (tb->len + 2 >= tb->alloc) {
1,806,154✔
1269
      tb->alloc *= 2;
213✔
1270
      tb->buf = xrealloc(tb->buf, tb->alloc);
213✔
1271
   }
1272

1273
   tb->buf[(tb->len)++] = ch;
1,806,154✔
1274
   tb->buf[tb->len] = '\0';
1,806,154✔
1275
}
1,806,154✔
1276

1277
void tb_catn(text_buf_t *tb, const char *str, size_t nchars)
3,010,240✔
1278
{
1279
   if (tb->len + nchars + 1 >= tb->alloc) {
3,010,240✔
1280
      tb->alloc = next_power_of_2(tb->alloc + nchars);
4,487✔
1281
      tb->buf = xrealloc(tb->buf, tb->alloc);
4,487✔
1282
   }
1283

1284
   memcpy(tb->buf + tb->len, str, nchars);
3,010,240✔
1285
   tb->len += nchars;
3,010,240✔
1286
   tb->buf[tb->len] = '\0';
3,010,240✔
1287
}
3,010,240✔
1288

1289
void tb_cat(text_buf_t *tb, const char *str)
1,164,194✔
1290
{
1291
   tb_catn(tb, str, strlen(str));
1,164,194✔
1292
}
1,164,194✔
1293

1294
void tb_repeat(text_buf_t *tb, char ch, size_t count)
×
1295
{
1296
   if (tb->len + count + 1 >= tb->alloc) {
×
1297
      tb->alloc = next_power_of_2(tb->alloc + count + 1);
×
1298
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1299
   }
1300

1301
   memset(tb->buf + tb->len, ch, count);
×
1302
   tb->len += count;
×
1303
   tb->buf[tb->len] = '\0';
×
1304
}
×
1305

1306
char *tb_reserve(text_buf_t *tb, size_t size)
×
1307
{
1308
   if (tb->len + size + 1 >= tb->alloc) {
×
1309
      tb->alloc = next_power_of_2(tb->alloc + size + 1);
×
1310
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1311
   }
1312

1313
   char *start = tb->buf + tb->len;
×
1314

1315
   tb->len += size;
×
1316
   tb->buf[tb->len] = '\0';
×
1317

1318
   return start;
×
1319
}
1320

1321
size_t tb_len(text_buf_t *tb)
151,606✔
1322
{
1323
   return tb->len;
151,606✔
1324
}
1325

1326
char *tb_claim(text_buf_t *tb)
48,334✔
1327
{
1328
   char *buf = tb->buf;
48,334✔
1329
   tb->buf = NULL;
48,334✔
1330
   return buf;
48,334✔
1331
}
1332

1333
const char *tb_get(text_buf_t *tb)
656,148✔
1334
{
1335
   return tb->buf;
656,148✔
1336
}
1337

1338
void tb_rewind(text_buf_t *tb)
86,555✔
1339
{
1340
   tb->len = 0;
86,555✔
1341
   tb->buf[0] = '\0';
86,555✔
1342
}
86,555✔
1343

1344
void tb_trim(text_buf_t *tb, size_t newlen)
1,169✔
1345
{
1346
   assert(newlen <= tb->len);
1,169✔
1347
   tb->len = newlen;
1,169✔
1348
   tb->buf[tb->len] = '\0';
1,169✔
1349
}
1,169✔
1350

1351
void tb_strip(text_buf_t *tb)
1✔
1352
{
1353
   while (tb->len > 0 && isspace_iso88591(tb->buf[tb->len - 1]))
7✔
1354
      tb->buf[--(tb->len)] = '\0';
6✔
1355
}
1✔
1356

1357
void tb_downcase(text_buf_t *tb)
146,432✔
1358
{
1359
   for (size_t i = 0; i < tb->len; i++)
25,557,759✔
1360
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
25,411,327✔
1361
}
146,432✔
1362

1363
void tb_upcase(text_buf_t *tb)
1,998✔
1364
{
1365
   for (size_t i = 0; i < tb->len; i++)
14,039✔
1366
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
12,041✔
1367
}
1,998✔
1368

1369
void tb_replace(text_buf_t *tb, char old, char rep)
696✔
1370
{
1371
   for (size_t i = 0; i < tb->len; i++) {
10,065✔
1372
      if (tb->buf[i] == old)
9,369✔
1373
         tb->buf[i] = rep;
696✔
1374
   }
1375
}
696✔
1376

1377
void tb_strftime(text_buf_t *tb, const char *fmt, time_t time)
×
1378
{
1379
   struct tm tm;
×
1380
   switch (fmt[0]) {
×
1381
   case 'L':
×
1382
#ifdef __MINGW32__
1383
      localtime_s(&tm, &time);
1384
#else
1385
      localtime_r(&time, &tm);
×
1386
#endif
1387
      break;
×
1388
   case 'G':
×
1389
#ifdef __MINGW32__
1390
      gmtime_s(&tm, &time);
1391
#else
1392
      gmtime_r(&time, &tm);
×
1393
#endif
1394
      break;
×
1395
   default:
×
1396
      fatal_trace("invalid timezone specifier '%c'", fmt[0]);
1397
   }
1398

1399
   const size_t max = 64;
×
1400
   char *p = tb_reserve(tb, max);
×
1401
   if (strftime(p, max, fmt + 1, &tm) == 0)
×
1402
      fatal_trace("time format buffer too small");
1403
}
×
1404

1405
void tb_ostream_write(const char *buf, size_t len, void *ctx)
1,337,068✔
1406
{
1407
   text_buf_t *tb = ctx;
1,337,068✔
1408
   tb_catn(tb, buf, len);
1,337,068✔
1409
}
1,337,068✔
1410

1411
void _local_free(void *ptr)
1,443,387✔
1412
{
1413
   free(*(void **)ptr);
1,443,387✔
1414
}
1,443,387✔
1415

1416
void set_message_style(message_style_t style)
×
1417
{
1418
   message_style = style;
×
1419

1420
   if (style == MESSAGE_COMPACT)
×
1421
      want_color = false;
×
1422
}
×
1423

1424
message_style_t get_message_style(void)
30,665✔
1425
{
1426
   return message_style;
30,665✔
1427
}
1428

1429
#ifndef __MINGW32__
1430
static uint64_t timeval_us(struct timeval *tv)
11,244✔
1431
{
1432
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
11,244✔
1433
}
1434
#endif
1435

1436
void nvc_rusage(nvc_rusage_t *ru)
5,622✔
1437
{
1438
#ifndef __MINGW32__
1439
   static uint64_t last_user, last_sys;
5,622✔
1440

1441
   struct rusage buf;
5,622✔
1442
   if (getrusage(RUSAGE_SELF, &buf) < 0)
5,622✔
1443
      fatal_errno("getrusage");
×
1444

1445
   const uint64_t user = timeval_us(&(buf.ru_utime));
5,622✔
1446
   const uint64_t sys = timeval_us(&(buf.ru_stime));
5,622✔
1447

1448
   ru->user = (user - last_user) / 1000;
5,622✔
1449
   ru->sys = (sys - last_sys) / 1000;
5,622✔
1450

1451
   last_sys = sys;
5,622✔
1452
   last_user = user;
5,622✔
1453

1454
#ifdef __APPLE__
1455
   const int rss_units = 1024;
1456
#else
1457
   const int rss_units = 1;
5,622✔
1458
#endif
1459

1460
   ru->rss = buf.ru_maxrss / rss_units;
5,622✔
1461
#else
1462
   static ULARGE_INTEGER last_kernel, last_user;
1463
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1464
   HANDLE hProcess = GetCurrentProcess();
1465

1466
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1467
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1468
      fatal_win32("GetProcessTimes");
1469

1470
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1471
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1472
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1473
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1474

1475
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1476
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1477

1478
   last_user = lv_Tuser;
1479
   last_kernel = lv_Tkernel;
1480

1481
   PROCESS_MEMORY_COUNTERS counters;
1482
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1483
      fatal_win32("GetProcessMemoryInfo");
1484

1485
   ru->rss = counters.PeakWorkingSetSize / 1024;
1486
#endif
1487

1488
   static uint64_t last_ts;
5,622✔
1489
   const uint64_t ts = get_timestamp_us();
5,622✔
1490
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
5,622✔
1491
   last_ts = ts;
5,622✔
1492
}
5,622✔
1493

1494
#ifdef __MINGW32__
1495
static uint64_t file_time_to_nanos(LPFILETIME ft)
1496
{
1497
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1498
   nanos |= ft->dwLowDateTime;
1499

1500
   // Windows file timestamps are in units of 100 nanoseconds since
1501
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1502
   // epoch 1970-01-01T00:00:00Z
1503
   nanos -= UINT64_C(116444736000000000);
1504
   nanos *= 100;
1505

1506
   return nanos;
1507
}
1508

1509
static bool fill_file_info(file_info_t *info, HANDLE handle)
1510
{
1511
   memset(info, '\0', sizeof(file_info_t));
1512

1513
   BY_HANDLE_FILE_INFORMATION hinfo;
1514
   if (!GetFileInformationByHandle(handle, &hinfo))
1515
      fatal_win32("GetFileInformationByHandle");
1516

1517
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1518
   info->size |= hinfo.nFileSizeLow;
1519

1520
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1521
      info->type = FILE_DIR;
1522
   else
1523
      info->type = FILE_REGULAR;
1524

1525
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1526

1527
   return true;
1528
}
1529
#else  // !__MINGW32__
1530
static void fill_file_info(file_info_t *info, const struct stat *st)
136,332✔
1531
{
1532
   memset(info, '\0', sizeof(file_info_t));
136,332✔
1533

1534
   info->size = st->st_size;
136,332✔
1535

1536
   if (S_ISDIR(st->st_mode))
136,332✔
1537
      info->type = FILE_DIR;
122✔
1538
   else if (!S_ISREG(st->st_mode))
136,210✔
1539
      info->type = FILE_FIFO;
72✔
1540
   else
1541
      info->type = FILE_REGULAR;
1542

1543
   info->mtime = st->st_mtime * UINT64_C(1000000000);
136,332✔
1544

1545
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1546
   info->mtime += st->st_mtimespec.tv_nsec;
1547
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1548
   info->mtime += st->st_mtim.tv_nsec;
136,332✔
1549
#endif
1550
}
136,332✔
1551
#endif  // !__MINGW32__
1552

1553
bool get_file_info(const char *path, file_info_t *info)
61,709✔
1554
{
1555
#ifdef __MINGW32__
1556
   HANDLE handle = CreateFile(
1557
        path, FILE_READ_ATTRIBUTES,
1558
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1559
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1560

1561
   if (handle == INVALID_HANDLE_VALUE)
1562
      return false;
1563

1564
   fill_file_info(info, handle);
1565

1566
   if (!CloseHandle(handle))
1567
      fatal_win32("CloseHandle");
1568

1569
   return true;
1570
#else
1571
   struct stat st;
61,709✔
1572
   if (stat(path, &st) == 0) {
61,709✔
1573
      fill_file_info(info, &st);
17,022✔
1574
      return true;
17,022✔
1575
   }
1576
   else
1577
      return false;
1578
#endif
1579
}
1580

1581
bool get_handle_info(int fd, file_info_t *info)
119,310✔
1582
{
1583
#ifdef __MINGW32__
1584
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1585
   fill_file_info(info, handle);
1586
   return true;
1587
#else
1588
   struct stat st;
119,310✔
1589
   if (fstat(fd, &st) == 0) {
119,310✔
1590
      fill_file_info(info, &st);
119,310✔
1591
      return true;
119,310✔
1592
   }
1593
   else
1594
      return false;
1595
#endif
1596
}
1597

1598
void run_program(const char *const *args)
×
1599
{
1600
#if defined __CYGWIN__ || defined __MINGW32__
1601
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1602
#else  // __CYGWIN__
1603
   pid_t pid = fork();
×
1604
   int status = 0;
×
1605
   if (pid == 0) {
×
1606
      execvp(args[0], (char *const *)args);
×
1607
      fatal_errno("execv");
×
1608
   }
1609
   else if (pid > 0) {
×
1610
      if (waitpid(pid, &status, 0) != pid)
×
1611
         fatal_errno("waitpid");
×
1612

1613
      status = WEXITSTATUS(status);
×
1614
   }
1615
   else
1616
      fatal_errno("fork");
×
1617
#endif  // __CYGWIN__
1618

1619
   if (status != 0) {
×
1620
      LOCAL_TEXT_BUF tb = tb_new();
×
1621
      for (size_t i = 0; args[i] != NULL; i++)
×
1622
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1623
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1624
   }
1625
}
×
1626

1627
char *nvc_temp_file(void)
×
1628
{
1629
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
×
1630
   const char *tmpdir = NULL;
×
1631
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
×
1632
      tmpdir = getenv(try[i]);
×
1633

1634
#ifdef __MINGW32__
1635
   char *buf = xasprintf("%s\\nvc-XXXXXX", tmpdir ?: ".");
1636
   return _mktemp(buf);
1637
#else
1638
   char *buf = xasprintf("%s/nvc-XXXXXX", tmpdir ?: "/tmp");
×
1639
   int fd = mkstemp(buf);
×
1640
   if (fd < 0)
×
1641
      fatal_errno("mkstemp");
×
1642
   close(fd);
×
1643
   return buf;
×
1644
#endif
1645
}
1646

1647
void file_read_lock(int fd)
55,866✔
1648
{
1649
#ifdef __MINGW32__
1650
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1651

1652
   LARGE_INTEGER li;
1653
   li.QuadPart = _filelengthi64(fd);
1654

1655
   OVERLAPPED ovlp;
1656
   memset(&ovlp, 0, sizeof ovlp);
1657

1658
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1659
      fatal_win32("LockFileEx");
1660
#else
1661
   if (flock(fd, LOCK_SH) < 0)
55,866✔
1662
      fatal_errno("flock");
×
1663
#endif
1664
}
55,866✔
1665

1666
void file_write_lock(int fd)
13,429✔
1667
{
1668
#ifdef __MINGW32__
1669
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1670

1671
   LARGE_INTEGER li;
1672
   li.QuadPart = _filelengthi64(fd);
1673

1674
   OVERLAPPED ovlp;
1675
   memset(&ovlp, 0, sizeof ovlp);
1676

1677
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1678
                   li.LowPart, li.HighPart, &ovlp))
1679
      fatal_win32("LockFileEx");
1680
#else
1681
   if (flock(fd, LOCK_EX) < 0)
13,429✔
1682
      fatal_errno("flock");
×
1683
#endif
1684
}
13,429✔
1685

1686
void file_unlock(int fd)
69,283✔
1687
{
1688
#ifdef __MINGW32__
1689
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1690

1691
   LARGE_INTEGER li;
1692
   li.QuadPart = _filelengthi64 (fd);
1693

1694
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1695
#else
1696
   if (flock(fd, LOCK_UN) < 0)
69,283✔
1697
      fatal_errno("flock");
×
1698
#endif
1699
}
69,283✔
1700

1701
void *map_file(int fd, size_t size)
63,436✔
1702
{
1703
#ifdef __MINGW32__
1704
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1705
                                     PAGE_READONLY, 0, size, NULL);
1706
   if (!handle)
1707
      fatal_win32("CreateFileMapping");
1708

1709
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1710
                               0, (SIZE_T) size, (LPVOID) NULL);
1711
   CloseHandle(handle);
1712
   if (ptr == NULL)
1713
      fatal_win32("MapViewOfFileEx");
1714
#else
1715
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
63,436✔
1716
   if (ptr == MAP_FAILED)
63,436✔
1717
      fatal_trace("mmap failed to map %zu byte file", size);
1718
#endif
1719
   return ptr;
63,436✔
1720
}
1721

1722
void unmap_file(void *ptr, size_t size)
56,156✔
1723
{
1724
#ifdef __MINGW32__
1725
   if (!UnmapViewOfFile((LPCVOID) ptr))
1726
      fatal_win32("UnmapViewOfFile");
1727
#else
1728
   munmap(ptr, size);
56,156✔
1729
#endif
1730
}
56,156✔
1731

1732
void make_dir(const char *fmt, ...)
6,358✔
1733
{
1734
   va_list ap;
6,358✔
1735
   va_start(ap, fmt);
6,358✔
1736
   char *path LOCAL = xvasprintf(fmt, ap);
12,716✔
1737
   va_end(ap);
6,358✔
1738

1739
#ifdef __MINGW32__
1740
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1741
      fatal_win32("mkdir: %s", path);
1742
#else
1743
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
6,358✔
1744
      fatal_errno("mkdir: %s", path);
×
1745
#endif
1746
}
6,358✔
1747

1748
FILE *create_file(const char *fmt, ...)
1,404✔
1749
{
1750
   va_list ap;
1,404✔
1751
   va_start(ap, fmt);
1,404✔
1752
   char *path LOCAL = xvasprintf(fmt, ap);
1,404✔
1753
   va_end(ap);
1,404✔
1754

1755
   FILE *f = fopen(path, "w");
1,404✔
1756
   if (f == NULL)
1,404✔
1757
      fatal_errno("cannot create %s", path);
×
1758

1759
   return f;
1,404✔
1760
}
1761

1762
uint64_t get_timestamp_ns(void)
68,781✔
1763
{
1764
#if defined __MINGW32__
1765
   static volatile uint64_t freq;
1766
   if (load_acquire(&freq) == 0) {
1767
      LARGE_INTEGER tmp;
1768
      if (!QueryPerformanceFrequency(&tmp))
1769
         fatal_win32("QueryPerformanceFrequency");
1770
      store_release(&freq, tmp.QuadPart);
1771
   }
1772

1773
   LARGE_INTEGER ticks;
1774
   if (!QueryPerformanceCounter(&ticks))
1775
      fatal_win32("QueryPerformanceCounter");
1776
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1777
#else
1778
   struct timespec ts;
68,781✔
1779
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
68,781✔
1780
      fatal_errno("clock_gettime");
×
1781
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
68,781✔
1782
#endif
1783
}
1784

1785
uint64_t get_timestamp_us(void)
68,781✔
1786
{
1787
   return get_timestamp_ns() / 1000;
68,781✔
1788
}
1789

1790
timestamp_t get_real_time(void)
23,749✔
1791
{
1792
#if defined __MINGW32__
1793
   FILETIME ft;
1794
   GetSystemTimeAsFileTime(&ft);
1795

1796
   return file_time_to_nanos(&ft);
1797
#else
1798
   struct timespec ts;
23,749✔
1799
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
23,749✔
1800
      fatal_errno("clock_gettime");
×
1801

1802
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
23,749✔
1803
#endif
1804
}
1805

1806
void open_pipe(int *rfd, int *wfd)
×
1807
{
1808
   int fds[2];
×
1809
#ifdef __MINGW32__
1810
   const int rc = _pipe(fds, 4096, _O_BINARY);
1811
#else
1812
   const int rc = pipe(fds) < 0;
×
1813
#endif
1814
   if (rc < 0)
×
1815
      fatal_errno("failed to create pipe");
1816

1817
   *rfd = fds[0];
×
1818
   *wfd = fds[1];
×
1819
}
×
1820

1821
#if defined _WIN32 || defined __CYGWIN__
1822
static struct {
1823
   char illegal;
1824
   const char *rep;
1825
} symbol_replacements[] = {
1826
   { '(', "_lp_"   },
1827
   { ')', "_rp_"   },
1828
   { '"', "_q_"    },
1829
   { '[', "_ls_"   },
1830
   { ']', "_rs_"   },
1831
   { '*', "_mult_" },
1832
   { '+', "_plus_" },
1833
   { '=', "_eq_"   },
1834
   { '\\', "_bs_"  },
1835
};
1836

1837
static text_buf_t *safe_symbol_win32(const char *text)
1838
{
1839
   text_buf_t *tb = tb_new();
1840

1841
   for (const char *p = text; *p != '\0'; p++) {
1842
      bool replaced = false;
1843
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
1844
         if (*p == symbol_replacements[j].illegal) {
1845
            tb_cat(tb, symbol_replacements[j].rep);
1846
            replaced = true;
1847
            break;
1848
         }
1849
      }
1850

1851
      if (!replaced)
1852
         tb_append(tb, *p);
1853
   }
1854

1855
   return tb;
1856
}
1857

1858
#endif
1859

1860
text_buf_t *safe_symbol(ident_t id)
×
1861
{
1862
   // Return a string that is safe to use as a symbol name on this platform
1863

1864
   text_buf_t *tb = tb_new();
×
1865
   tb_istr(tb, id);
×
1866

1867
#if defined _WIN32 || defined __CYGWIN__
1868
   if (strpbrk(tb_get(tb), "()\"[]*+=\\") == NULL)
1869
      return tb;
1870
   else {
1871
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
1872
      tb_free(tb);
1873
      return new;
1874
   }
1875
#else
1876
   return tb;
×
1877
#endif
1878
}
1879

1880
void __cleanup_array(void *ptr)
247,590✔
1881
{
1882
   A(void *) *a = ptr;
247,590✔
1883
   ACLEAR(*a);
247,590✔
1884
}
247,590✔
1885

1886
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
2,190,695✔
1887
                         size_t size)
1888
{
1889
   if (count == 0) {
2,190,695✔
1890
      free(*ptr);
×
1891
      *ptr = NULL;
×
1892
      *limit = 0;
×
1893
   }
1894
   else {
1895
      if (*limit == 0)
2,190,695✔
1896
         *limit = count;  // Setting the initial size of the array
1,907,380✔
1897
      else
1898
         *limit = next_power_of_2(count);
283,315✔
1899
      *ptr = xrealloc_array(*ptr, *limit, size);
2,190,695✔
1900
   }
1901
}
2,190,695✔
1902

1903
bool get_exe_path(text_buf_t *tb)
8,198✔
1904
{
1905
#if defined __linux__
1906
   char buf[PATH_MAX];
8,198✔
1907
   ssize_t nchars = readlink("/proc/self/exe", buf, sizeof(buf));
8,198✔
1908
   if (nchars > 0) {   // Does not append '\0'
8,198✔
1909
      tb_catn(tb, buf, nchars);
8,198✔
1910
      return true;
8,198✔
1911
   }
1912
#elif defined __APPLE__
1913
   char buf[PATH_MAX];
1914
   if (proc_pidpath(getpid(), buf, sizeof(buf)) > 0) {
1915
      tb_cat(tb, buf);
1916
      return true;
1917
   }
1918
#elif defined __FreeBSD__
1919
   char buf[PATH_MAX];
1920
   size_t size = sizeof(buf);
1921
   const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
1922
   if (sysctl(name, ARRAY_LEN(name), buf, &size, NULL, 0) == 0) {
1923
      tb_catn(tb, buf, size);
1924
      return true;
1925
   }
1926
#elif defined __MINGW32__
1927
   HANDLE hProc = GetCurrentProcess();
1928
   char buf[PATH_MAX];
1929
   DWORD size = sizeof(buf);
1930
   if (QueryFullProcessImageNameA(hProc, 0, buf, &size)) {
1931
      tb_cat(tb, buf);
1932
      return true;
1933
   }
1934
#endif
1935
   return false;
1936
}
1937

1938
#if defined __MINGW32__
1939
static void get_relative_prefix(text_buf_t *tb)
1940
{
1941
   if (get_exe_path(tb)) {
1942
      int len = tb_len(tb);
1943
      const char *str = tb_get(tb);
1944
      for (int i = 0; i < 2; i++) {
1945
         do {
1946
            len--;
1947
         } while (str[len] != DIR_SEP[0]);
1948
      }
1949
      tb_trim(tb, len);
1950
   }
1951
   else
1952
      fatal("failed to read executable path");
1953
}
1954
#endif
1955

1956
void get_libexec_dir(text_buf_t *tb)
×
1957
{
1958
#if defined __MINGW32__
1959
   get_relative_prefix(tb);
1960
   tb_cat(tb, DIR_SEP "libexec" DIR_SEP "nvc");
1961
#else
1962
   tb_cat(tb, LIBEXECDIR);
×
1963
#endif
1964
}
×
1965

1966
void get_lib_dir(text_buf_t *tb)
8,564✔
1967
{
1968
#if defined __MINGW32__
1969
   get_relative_prefix(tb);
1970
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
1971
#else
1972
   tb_cat(tb, LIBDIR);
8,564✔
1973
#endif
1974
}
8,564✔
1975

1976
void get_data_dir(text_buf_t *tb)
31✔
1977
{
1978
#if defined __MINGW32__
1979
   get_relative_prefix(tb);
1980
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
1981
#else
1982
   tb_cat(tb, DATADIR);
31✔
1983
#endif
1984
}
31✔
1985

1986
bool is_absolute_path(const char *path)
32✔
1987
{
1988
   if (path[0] == DIR_SEP[0] || path[0] == '/')
32✔
1989
      return true;
32✔
1990

1991
#ifdef __MINGW32__
1992
   if (isalpha((int)path[0]) && path[1] == ':')
1993
      return true;
1994
#endif
1995

1996
   return false;
1997
}
1998

1999
void get_relative_path(text_buf_t *tb, const char *from, const char *to)
295✔
2000
{
2001
#ifdef __MINGW32__
2002
    char abs_from[MAX_PATH], abs_to[MAX_PATH];
2003
    if (from == NULL || !_fullpath(abs_from, from, MAX_PATH))
2004
       goto fallback;
2005
    if (!_fullpath(abs_to, to, MAX_PATH))
2006
       goto fallback;
2007

2008
    char buffer[MAX_PATH];
2009
    if (!PathRelativePathToA(buffer, abs_from, FILE_ATTRIBUTE_DIRECTORY,
2010
                             abs_to, 0))
2011
       goto fallback;
2012

2013
    if (strncmp(buffer, ".\\", 2) == 0)
2014
       tb_cat(tb, buffer + 2);   // Strip leading .\ prefix
2015
    else
2016
       tb_cat(tb, buffer);
2017
#else
2018
   char abs_from[PATH_MAX], abs_to[PATH_MAX];
295✔
2019
   if (from == NULL || realpath(from, abs_from) == NULL)
550✔
2020
      goto fallback;
40✔
2021
   else if (realpath(to, abs_to) == NULL)
255✔
2022
      goto fallback;
×
2023

2024
   A(char *) from_parts = AINIT;
255✔
2025
   A(char *) to_parts = AINIT;
255✔
2026

2027
   for (char *tok = strtok(abs_from, "/"); tok; tok = strtok(NULL, "/"))
1,232✔
2028
      APUSH(from_parts, tok);
977✔
2029

2030
   for (char *tok = strtok(abs_to, "/"); tok; tok = strtok(NULL, "/"))
1,785✔
2031
      APUSH(to_parts, tok);
1,530✔
2032

2033
   int common = 0;
2034
   while (common < from_parts.count && common < to_parts.count
977✔
2035
          && strcmp(from_parts.items[common], to_parts.items[common]) == 0)
2,209✔
2036
      common++;
977✔
2037

2038
   for (int i = common; i < from_parts.count; i++) {
255✔
2039
      tb_cat(tb, "..");
×
2040
      if (i + 1 < from_parts.count || to_parts.count > common)
×
2041
         tb_cat(tb, "/");
×
2042
   }
2043

2044
   for (int i = common; i < to_parts.count; i++) {
808✔
2045
      tb_cat(tb, to_parts.items[i]);
553✔
2046
      if (i + 1 < to_parts.count)
553✔
2047
         tb_cat(tb, "/");
298✔
2048
   }
2049

2050
   ACLEAR(from_parts);
255✔
2051
   ACLEAR(to_parts);
255✔
2052
#endif
2053

2054
   if (tb_len(tb) == 0)
255✔
2055
      goto fallback;
×
2056

2057
   return;
255✔
2058

2059
 fallback:
40✔
2060
   tb_cat(tb, to);
40✔
2061
}
2062

2063
void get_hex_hash(const char *str, char out[SHA_HEX_LEN])
1,134✔
2064
{
2065
   SHA1_CTX ctx;
1,134✔
2066
   unsigned char hash[SHA1_LEN];
1,134✔
2067

2068
   SHA1Init(&ctx);
1,134✔
2069
   SHA1Update(&ctx, (const unsigned char *)str, strlen(str));
1,134✔
2070
   SHA1Final(hash, &ctx);
1,134✔
2071

2072
   for (int i = 0; i < SHA1_LEN; i++)
23,814✔
2073
      snprintf(out + i * 2, 3, "%02x", hash[i]);
22,680✔
2074
}
1,134✔
2075

2076
void progress(const char *fmt, ...)
19,753✔
2077
{
2078
   if (opt_get_int(OPT_VERBOSE)) {
19,753✔
2079
      va_list ap;
×
2080
      va_start(ap, fmt);
×
2081
      char *msg LOCAL = xvasprintf(fmt, ap);
×
2082
      va_end(ap);
×
2083

2084
      static nvc_rusage_t last_ru;
×
2085

2086
      nvc_rusage_t ru;
×
2087
      nvc_rusage(&ru);
×
2088

2089
      const double conc = (double)(ru.user + ru.sys) / ru.ms;
×
2090

2091
      if (!isinf(conc) && conc > 1.1)
×
2092
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
2093
               ru.rss - last_ru.rss);
×
2094
      else
2095
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
×
2096

2097
      last_ru = ru;
×
2098
   }
2099
}
19,753✔
2100

2101
unsigned nvc_nprocs(void)
9,104✔
2102
{
2103
#if defined _WIN32
2104
   SYSTEM_INFO sysinfo;
2105
   GetSystemInfo(&sysinfo);
2106

2107
   return sysinfo.dwNumberOfProcessors;
2108
#elif defined _SC_NPROCESSORS_ONLN
2109
   long count = sysconf(_SC_NPROCESSORS_ONLN);
9,104✔
2110
   if (count == -1)
9,104✔
2111
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2112

2113
#if defined __linux__ && defined HAVE_GETTID
2114
   // Restrict to the number of CPUs we are allowed to run on
2115
   cpu_set_t s;
9,104✔
2116
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
9,104✔
2117
      return MAX(1, MIN(count, CPU_COUNT(&s)));
9,104✔
2118
#endif
2119

2120
   return count;
×
2121
#else
2122
#warning Cannot detect number of processors on this platform
2123
   return 1;
2124
#endif
2125
}
2126

2127
void capture_registers(struct cpu_state *cpu)
879✔
2128
{
2129
#if defined HAVE_GETCONTEXT
2130
   ucontext_t uc;
879✔
2131
   if (getcontext(&uc) != 0)
879✔
2132
      fatal_errno("getcontext");
×
2133

2134
   fill_cpu_state(cpu, &uc);
879✔
2135
#elif defined __MINGW32__
2136
   CONTEXT context;
2137
   RtlCaptureContext(&context);
2138
   fill_cpu_state(cpu, &context);
2139
#elif defined HAVE_PTHREAD
2140
   assert(atomic_load(&thread_regs) == NULL);
2141
   atomic_store(&thread_regs, cpu);
2142

2143
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2144
      fatal_errno("pthread_kill");
2145

2146
   // Registers filled in by signal_handler
2147
   if (atomic_load(&thread_regs) != NULL)
2148
      fatal_trace("signal handler did not capture thread registers");
2149
#else
2150
#error cannot capture registers on this platform
2151
#endif
2152
}
879✔
2153

2154
void add_fault_handler(fault_fn_t fn, void *context)
14,494✔
2155
{
2156
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
14,494✔
2157
   h->next    = fault_handlers;
14,494✔
2158
   h->fn      = fn;
14,494✔
2159
   h->context = context;
14,494✔
2160

2161
   fault_handlers = h;
14,494✔
2162
}
14,494✔
2163

2164
void remove_fault_handler(fault_fn_t fn, void *context)
5,850✔
2165
{
2166
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
5,856✔
2167
      if ((*p)->fn == fn && (*p)->context == context) {
5,856✔
2168
         fault_handler_t *tmp = (*p)->next;
5,850✔
2169
         free(*p);
5,850✔
2170
         *p = tmp;
5,850✔
2171
         return;
5,850✔
2172
      }
2173
   }
2174

2175
   fatal_trace("no fault handler for %p with context %p", fn, context);
2176
}
2177

2178
void check_cpu_features(void)
7,976✔
2179
{
2180
#ifdef HAVE_POPCNT
2181
   if (!__builtin_cpu_supports("popcnt"))
7,976✔
2182
      fatal("CPU is missing support for POPCNT instruction");
×
2183
#endif
2184
}
7,976✔
2185

2186
#ifdef DEBUG
2187
void should_not_reach_here(void)
2188
{
2189
   fatal_trace("should not reach here");
2190
}
2191
#endif
2192

2193
mem_pool_t *pool_new(void)
44,486✔
2194
{
2195
   mem_pool_t *mp = xcalloc(sizeof(mem_pool_t));
44,486✔
2196
   mp->pageshift = POOL_PAGE_MIN;
44,486✔
2197

2198
   return mp;
44,486✔
2199
}
2200

2201
void pool_free(mem_pool_t *mp)
119,634✔
2202
{
2203
   if (mp == NULL)
119,634✔
2204
      return;
2205

2206
   for (pool_page_t *p = mp->pages, *tmp; p != NULL; p = tmp) {
74,357✔
2207
      tmp = p->next;
34,812✔
2208
      free(p);
34,812✔
2209
   }
2210

2211
   free(mp);
39,545✔
2212
}
2213

2214
static pool_page_t *pool_page_new(mem_pool_t *mp, size_t reqsz, size_t align)
38,465✔
2215
{
2216
   const size_t hdrsz = ALIGN_UP(sizeof(page_header_t), align);
38,465✔
2217
   const size_t minsz = next_power_of_2(reqsz + hdrsz);
38,465✔
2218
   const size_t allocsz = MAX(1 << mp->pageshift, minsz);
38,465✔
2219
   page_header_t *p = xmalloc(allocsz);
38,465✔
2220
   p->next  = mp->pages;
38,465✔
2221
   p->alloc = hdrsz;
38,465✔
2222
   p->size  = allocsz;
38,465✔
2223

2224
   ASAN_POISON(p + hdrsz, allocsz - hdrsz);
38,465✔
2225

2226
   if (mp->pageshift < POOL_PAGE_MAX)
38,465✔
2227
      mp->pageshift++;
38,465✔
2228

2229
   return (mp->pages = p);
38,465✔
2230
}
2231

2232
static void *pool_aligned_malloc(mem_pool_t *mp, size_t size, size_t align)
1,420,729✔
2233
{
2234
   assert(is_power_of_2(align));
1,420,729✔
2235

2236
   pool_page_t *page;
1,420,729✔
2237
   if (mp->pages == NULL)
1,420,729✔
2238
      page = pool_page_new(mp, size, align);
31,500✔
2239
   else {
2240
      const size_t base = ALIGN_UP(mp->pages->alloc + POOL_REDZONE, align);
1,389,229✔
2241
      if (base + size > mp->pages->size)
1,389,229✔
2242
         page = pool_page_new(mp, size, align);
6,965✔
2243
      else {
2244
         page = mp->pages;
1,382,264✔
2245
         page->alloc = base;
1,382,264✔
2246
      }
2247
   }
2248

2249
   assert((mp->pages->alloc & (align - 1)) == 0);
1,420,729✔
2250
   assert(mp->pages->alloc + size <= mp->pages->size);
1,420,729✔
2251
   assert(mp->pages->alloc >= sizeof(page_header_t));
1,420,729✔
2252

2253
   void *ptr = (void *)mp->pages + mp->pages->alloc;
1,420,729✔
2254
   mp->pages->alloc += size;
1,420,729✔
2255

2256
   ASAN_UNPOISON(ptr, size);
1,420,729✔
2257
   return ptr;
1,420,729✔
2258
}
2259

2260
void *pool_malloc(mem_pool_t *mp, size_t size)
1,027,332✔
2261
{
2262
   return pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
1,027,332✔
2263
}
2264

2265
void *pool_malloc_flex(mem_pool_t *mp, size_t fixed, size_t nelems,
969,748✔
2266
                       size_t size)
2267
{
2268
   size_t bytes;
969,748✔
2269
   if (__builtin_mul_overflow(nelems, size, &bytes))
969,748✔
2270
      fatal_trace("array size overflow: requested %zd * %zd bytes",
2271
                  nelems, size);
2272

2273
   return pool_malloc(mp, fixed + bytes);
969,748✔
2274
}
2275

2276
void *pool_malloc_array(mem_pool_t *mp, size_t nelems, size_t size)
19,350✔
2277
{
2278
   return pool_malloc_flex(mp, 0, nelems, size);
19,350✔
2279
}
2280

2281
void *pool_calloc(mem_pool_t *mp, size_t size)
393,397✔
2282
{
2283
   void *ptr = pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
393,397✔
2284
   memset(ptr, '\0', size);
393,397✔
2285
   return ptr;
393,397✔
2286
}
2287

2288
void pool_stats(mem_pool_t *mp, size_t *alloc, size_t *npages)
11,623✔
2289
{
2290
   *npages = 0;
11,623✔
2291
   *alloc = 0;
11,623✔
2292

2293
   for (pool_page_t *p = mp->pages; p != NULL; p = p->next) {
19,018✔
2294
      *npages += 1;
7,395✔
2295
      *alloc += p->alloc - sizeof(pool_page_t);
7,395✔
2296
   }
2297
}
11,623✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc