Coverage Report

Created: 2024-08-18 16:21

/home/liu/actions-runner/_work/ccv/ccv/test/case_main.h
Line
Count
Source (jump to first uncovered line)
1
#if !defined(_GUARD_case_main_h_) && !defined(CASE_DISABLE_MAIN)
2
#define _GUARD_case_main_h_
3
4
#include <string.h>
5
#include <assert.h>
6
7
static int case_print_hi(char* str, const char* const hi)
8
0
{
9
0
  if (!hi)
10
0
    return printf("%s", str);
11
0
  const size_t hilen = strlen(hi);
12
0
  char* savestr = strstr(str, hi);
13
0
  int nchr = 0;
14
0
  while (savestr)
15
0
  {
16
0
    for (;str < savestr; ++str, ++nchr)
17
0
      putchar(str[0]);
18
0
    nchr += printf("\033[7m%s\033[0m", hi); // decorate with underline.
19
0
    str += hilen;
20
0
    savestr = strstr(str, hi);
21
0
  }
22
0
  nchr += printf("%s", str);
23
0
  return nchr;
24
0
}
25
26
static void case_run(case_t* test_case, const char* const match_test, int i, int total, int* pass, int* skip, int* fail)
27
1.08k
{
28
  // Change the current directory.
29
1.08k
  if (test_case->dir && test_case->dir[0] != 0 && strcmp(test_case->dir, ".") != 0)
30
1.08k
    chdir(test_case->dir);
31
1.08k
  int clr = 0;
32
1.08k
  if (isatty(fileno(stdout)))
33
0
  {
34
0
    clr += printf("\033[0;34m[%d/%d]\033[0;0m \033[1;33m[RUN]\033[0;0m ", i + 1, total);
35
0
    clr += case_print_hi(test_case->name, match_test);
36
0
    clr += printf(" ...");
37
0
  } else
38
1.08k
    clr += printf("[%d/%d] [RUN] %s ...", i + 1, total, test_case->name);
39
1.08k
  fflush(stdout);
40
1.08k
  int result = 0;
41
1.08k
  test_case->func(test_case->name, &result);
42
1.08k
  if (result == 0)
43
937
  {
44
937
    (*pass)++;
45
63.1k
    for (; clr > 0; 
--clr62.2k
)
46
62.2k
      printf("\b");
47
937
    if (isatty(fileno(stdout)))
48
0
    {
49
0
      printf("\r\033[0;34m[%d/%d]\033[0;0m \033[1;32m[PASS]\033[0;0m ", i + 1, total);
50
0
      case_print_hi(test_case->name, match_test);
51
0
      printf("    \n");
52
0
    } else
53
937
      printf("\r[%d/%d] [PASS] %s    \n", i + 1, total, test_case->name);
54
937
  } else 
if (147
result == -2147
) {
55
147
    (*skip)++;
56
147
    if (isatty(fileno(stdout)))
57
0
    {
58
0
      printf("\n\033[0;34m[%d/%d]\033[0;0m \033[1;32m[SKIP]\033[0;0m ", i + 1, total);
59
0
      case_print_hi(test_case->name, match_test);
60
0
      printf("\n");
61
0
    } else
62
147
      printf("\n[%d/%d] [SKIP] %s\n", i + 1, total, test_case->name);
63
147
  } else {
64
0
    (*fail)++;
65
0
    if (isatty(fileno(stdout)))
66
0
    {
67
0
      printf("\n\033[0;34m[%d/%d]\033[0;0m \033[1;31m[FAIL]\033[0;0m ", i + 1, total);
68
0
      case_print_hi(test_case->name, match_test);
69
0
      printf("\n");
70
0
    } else
71
0
      printf("\n[%d/%d] [FAIL] %s\n", i + 1, total, test_case->name);
72
0
  }
73
1.08k
}
74
75
static void case_conclude(int pass, int skip, int fail)
76
1
{
77
1
  if (isatty(fileno(stdout)))
78
0
  {
79
0
    if (skip > 0)
80
0
    {
81
0
      if (fail == 0)
82
0
        printf("\033[0;32mall test case(s) passed, %d test case(s) skipped, congratulations!\033[0;0m\n", skip);
83
0
      else
84
0
        printf("\033[0;31m%d of %d test case(s) passed, %d test case(s) skipped\033[0;0m\n", pass, fail + pass, skip);
85
0
    } else {
86
0
      if (fail == 0)
87
0
        printf("\033[0;32mall test case(s) passed, congratulations!\033[0;0m\n");
88
0
      else
89
0
        printf("\033[0;31m%d of %d test case(s) passed\033[0;0m\n", pass, fail + pass);
90
0
    }
91
1
  } else {
92
1
    if (skip > 0)
93
1
    {
94
1
      if (fail == 0)
95
1
        printf("all test case(s) passed, %d test case(s) skipped, congratulations!\n", skip);
96
0
      else
97
0
        printf("%d of %d test case(s) passed, %d test case(s) skipped\n", pass, fail + pass, skip);
98
1
    } else {
99
0
      if (fail == 0)
100
0
        printf("all test case(s) passed, congratulations!\n");
101
0
      else
102
0
        printf("%d of %d test case(s) passed\n", pass, fail + pass);
103
0
    }
104
1
  }
105
1
}
106
107
#if defined(__ELF__) || defined(__APPLE__)
108
// in ELF object format, we can simply query custom section rather than scan through the whole binary memory
109
// to find function pointer. We do this whenever possible because in this way, we don't have access error
110
// when hooking up with memory checkers such as address sanitizer or valgrind
111
112
#ifdef __ELF__
113
static case_t __test_case_ctx_probe __attribute__((used, section("case_data_probe"), aligned(8))) = {0};
114
115
extern case_t __start_case_data[];
116
extern case_t __stop_case_data[];
117
118
extern case_t __start_case_data_probe[];
119
extern case_t __stop_case_data_probe[];
120
#else
121
#include <mach-o/dyld.h>
122
#include <mach-o/getsect.h>
123
#ifdef __LP64__
124
  typedef struct section_64 case_section;
125
  typedef struct mach_header_64 case_header;
126
#else
127
  typedef struct section case_section;
128
  typedef struct mach_header case_header;
129
#endif
130
static case_t __test_case_ctx_probe __attribute__((used, section("__DATA,case_data_probe"), aligned(8))) = {0};
131
#endif
132
133
int main(int argc, char** argv)
134
1
{
135
1
#ifdef __ELF__
136
1
  int case_size = (intptr_t)__stop_case_data_probe - (intptr_t)__start_case_data_probe;
137
1
  int test_size = (intptr_t)__stop_case_data - (intptr_t)__start_case_data;
138
1
  unsigned char* start_case_data = (unsigned char*)__start_case_data;
139
1
  unsigned char* stop_case_data = (unsigned char*)__stop_case_data;
140
#else
141
  uint32_t image_count = _dyld_image_count();
142
  int case_size = 0;
143
  for (uint32_t image_index = 0; !case_size && image_index < image_count; image_index++)
144
  {
145
    const case_header* mach_header = (const case_header*)_dyld_get_image_header(image_index);
146
    unsigned long size;
147
    uint8_t *data = getsectiondata(mach_header, "__DATA", "case_data_probe", &size);
148
    if (data)
149
      case_size = (int)size;
150
  }
151
  int test_size = 0;
152
  unsigned char* start_case_data = 0;
153
  unsigned char* stop_case_data = 0;
154
  for (uint32_t image_index = 0; image_index < image_count; image_index++)
155
  {
156
    const case_header* mach_header = (const case_header*)_dyld_get_image_header(image_index);
157
    unsigned long size;
158
    uint8_t *data = getsectiondata(mach_header, "__DATA", "case_data", &size);
159
    if (!data)
160
      continue;
161
    start_case_data = data;
162
    stop_case_data = data + size;
163
    test_size = (int)size;
164
    break;
165
  }
166
#endif
167
1
  char buf[1024];
168
1
  char* cur_dir = getcwd(buf, 1024);
169
1
  static uint64_t the_sig = 0x883253372849284B;
170
1
  int scan_mode = (test_size % case_size != 0);
171
1
  const char* match_test = (argc == 2) ? 
argv[1]0
: 0;
172
1
  int i, total = 0;
173
1
  if (!scan_mode)
174
1
    total = test_size / case_size;
175
1.08k
  for (i = 0; i < total; 
i++1.08k
)
176
1.08k
  {
177
1.08k
    case_t* test_case = (case_t*)(start_case_data + i * case_size);
178
    // If it doesn't match well, fallback to scan mode.
179
1.08k
    if (test_case->sig_head != the_sig || test_case->sig_tail != the_sig + 2)
180
0
    {
181
0
      scan_mode = 1;
182
0
      break;
183
0
    }
184
1.08k
  }
185
1
  int len, pass = 0, skip = 0, fail = 0;
186
  // In scan mode, we will scan the whole section for a matching test case.
187
1
  if (scan_mode)
188
0
  {
189
0
    total = 0;
190
0
    len = (intptr_t)stop_case_data - (intptr_t)start_case_data - sizeof(case_t) + 1;
191
0
    for (i = 0; i < len; i++)
192
0
    {
193
0
      case_t* test_case = (case_t*)(start_case_data + i);
194
0
      if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
195
0
        (!match_test || strstr(test_case->name, match_test)))
196
0
        total++;
197
0
    }
198
0
  }
199
1
  if (__test_case_setup)
200
1
    __test_case_setup();
201
1
  if (scan_mode)
202
0
  {
203
0
    int j = 0;
204
0
    for (i = 0; i < len; i++)
205
0
    {
206
0
      case_t* test_case = (case_t*)(start_case_data + i);
207
0
      if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
208
0
        (!match_test || strstr(test_case->name, match_test)))
209
0
      {
210
0
        case_run(test_case, match_test, j++, total, &pass, &skip, &fail);
211
0
        chdir(cur_dir);
212
0
      }
213
0
    }
214
1
  } else {
215
1
    int matched_total = match_test ? 
00
: total;
216
1
    if (match_test)
217
0
      for (i = 0; i < total; i++)
218
0
      {
219
0
        case_t* test_case = (case_t*)(start_case_data + i * case_size);
220
0
        if (strstr(test_case->name, match_test))
221
0
          matched_total++;
222
0
      }
223
1
    int j = 0;
224
    // Simple case, I don't need to scan the data section.
225
1.08k
    for (i = 0; i < total; 
i++1.08k
)
226
1.08k
    {
227
1.08k
      case_t* test_case = (case_t*)(start_case_data + i * case_size);
228
1.08k
      if (!match_test || 
strstr(test_case->name, match_test)0
)
229
1.08k
        case_run(test_case, match_test, j++, matched_total, &pass, &skip, &fail);
230
1.08k
      chdir(cur_dir);
231
1.08k
    }
232
1
  }
233
1
  if (__test_case_teardown)
234
0
    __test_case_teardown();
235
1
  case_conclude(pass, skip, fail);
236
1
  return fail;
237
1
}
238
239
#else
240
241
#include <stdio.h>
242
#include <unistd.h>
243
#include <stdlib.h>
244
#include <fcntl.h>
245
#include <ctype.h>
246
#include <signal.h>
247
#include <setjmp.h>
248
249
/* the following functions come from Hans Boehm's conservative gc with slightly
250
 * modifications, here is the licence:
251
 * Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers
252
 * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
253
 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
254
 * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
255
256
 * The file linux_threads.c is also
257
 * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
258
259
 * The files Makefile.am, and configure.in are
260
 * Copyright (c) 2001 by Red Hat Inc. All rights reserved.
261
262
 * Several files supporting GNU-style builds are copyrighted by the Free
263
 * Software Foundation, and carry a different license from that given
264
 * below.
265
266
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
267
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
268
269
 * Permission is hereby granted to use or copy this program
270
 * for any purpose,  provided the above notices are retained on all copies.
271
 * Permission to modify the code and to distribute modified code is granted,
272
 * provided the above notices are retained, and a notice that the code was
273
 * modified is included with the above copyright notice.
274
275
 * A few of the files needed to use the GNU-style build procedure come with
276
 * slightly different licenses, though they are all similar in spirit.  A few
277
 * are GPL'ed, but with an exception that should cover all uses in the
278
 * collector. */
279
280
/* Repeatedly perform a read call until the buffer is filled or */
281
/* we encounter EOF.            */
282
static ssize_t case_repeat_read(int fd, char *buf, size_t count)
283
{
284
    ssize_t num_read = 0;
285
    ssize_t result;
286
    
287
    while (num_read < count)
288
  {
289
    result = read(fd, buf + num_read, count - num_read);
290
    if (result < 0)
291
      return result;
292
    if (result == 0)
293
      break;
294
    num_read += result;
295
    }
296
    return num_read;
297
}
298
299
/* Determine the length of a file by incrementally reading it into a buffer */
300
/* This would be silly to use on a file supporting lseek, but Linux */
301
/* /proc files usually do not.  */
302
static size_t case_get_file_len(int f)
303
{
304
    size_t total = 0;
305
    ssize_t result;
306
    char buf[500];
307
308
    do {
309
    result = read(f, buf, 500);
310
    if (result == -1)
311
      return 0;
312
    total += result;
313
    } while (result > 0);
314
    return total;
315
}
316
317
static size_t case_get_maps_len(void)
318
{
319
    int f = open("/proc/self/maps", O_RDONLY);
320
    size_t result = case_get_file_len(f);
321
    close(f);
322
    return result;
323
}
324
325
/*
326
 * Copy the contents of /proc/self/maps to a buffer in our address space.
327
 * Return the address of the buffer, or zero on failure.
328
 * This code could be simplified if we could determine its size
329
 * ahead of time.
330
 */
331
static char* case_get_maps()
332
{
333
    int f;
334
    int result;
335
    static char init_buf[1];
336
    static char *maps_buf = init_buf;
337
    static size_t maps_buf_sz = 1;
338
    size_t maps_size, old_maps_size = 0;
339
340
    /* Note that in the presence of threads, the maps file can  */
341
    /* essentially shrink asynchronously and unexpectedly as  */
342
    /* threads that we already think of as dead release their */
343
    /* stacks.  And there is no easy way to read the entire */
344
    /* file atomically.  This is arguably a misfeature of the */
345
    /* /proc/.../maps interface.        */
346
347
    /* Since we dont believe the file can grow      */
348
    /* asynchronously, it should suffice to first determine */
349
    /* the size (using lseek or read), and then to reread the */
350
    /* file.  If the size is inconsistent we have to retry. */
351
    /* This only matters with threads enabled, and if we use  */
352
    /* this to locate roots (not the default).      */
353
354
    /* Determine the initial size of /proc/self/maps.   */
355
    /* Note that lseek doesn't work, at least as of 2.6.15. */
356
  maps_size = case_get_maps_len();
357
  if (0 == maps_size)
358
    return 0;
359
360
    /* Read /proc/self/maps, growing maps_buf as necessary. */
361
    /* Note that we may not allocate conventionally, and  */
362
    /* thus can't use stdio.          */
363
  do {
364
      while (maps_size >= maps_buf_sz)
365
    {
366
      /* Grow only by powers of 2, since we leak "too small" buffers. */
367
      while (maps_size >= maps_buf_sz)
368
        maps_buf_sz *= 2;
369
      maps_buf = malloc(maps_buf_sz);
370
      /* Recompute initial length, since we allocated.  */
371
      /* This can only happen a few times per program   */
372
      /* execution.           */
373
      maps_size = case_get_maps_len();
374
      if (0 == maps_size)
375
        return 0;
376
      }
377
      f = open("/proc/self/maps", O_RDONLY);
378
      if (-1 == f)
379
      return 0;
380
    old_maps_size = maps_size;
381
      maps_size = 0;
382
      do {
383
      result = case_repeat_read(f, maps_buf, maps_buf_sz - 1);
384
      if (result <= 0)
385
        return 0;
386
      maps_size += result;
387
      } while (result == maps_buf_sz - 1);
388
      close(f);
389
  } while (maps_size >= maps_buf_sz || maps_size < old_maps_size);
390
  /* In the single-threaded case, the second clause is false. */
391
    maps_buf[maps_size] = '\0';
392
  
393
    /* Apply fn to result. */
394
  return maps_buf;
395
}
396
397
//
398
//  GC_parse_map_entry parses an entry from /proc/self/maps so we can
399
//  locate all writable data segments that belong to shared libraries.
400
//  The format of one of these entries and the fields we care about
401
//  is as follows:
402
//  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
403
//  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
404
//  start    end      prot          maj_dev
405
//
406
//  Note that since about august 2003 kernels, the columns no longer have
407
//  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
408
//  anywhere, which is safer anyway.
409
//
410
411
/*
412
 * Assign various fields of the first line in buf_ptr to *start, *end,
413
 * *prot, *maj_dev and *mapping_name.  Mapping_name may be NULL.
414
 * *prot and *mapping_name are assigned pointers into the original
415
 * buffer.
416
 */
417
static char* case_parse_map_entry(char* buf_ptr, void** start, void** end, char** prot, unsigned int* maj_dev, char** mapping_name)
418
{
419
    char *start_start, *end_start, *maj_dev_start;
420
    char *p;
421
    char *endp;
422
423
    if (buf_ptr == NULL || *buf_ptr == '\0')
424
        return NULL;
425
426
    p = buf_ptr;
427
    while (isspace(*p))
428
    ++p;
429
    start_start = p;
430
    *start = (void*)strtoul(start_start, &endp, 16);
431
  p = endp;
432
433
    ++p;
434
    end_start = p;
435
    *end = (void*)strtoul(end_start, &endp, 16);
436
  p = endp;
437
438
    while (isspace(*p))
439
    ++p;
440
    *prot = p;
441
    /* Skip past protection field to offset field */
442
  while (!isspace(*p))
443
    ++p;
444
  while (isspace(*p))
445
    ++p;
446
    /* Skip past offset field, which we ignore */
447
  while (!isspace(*p))
448
    ++p;
449
  while (isspace(*p))
450
    ++p;
451
    maj_dev_start = p;
452
    *maj_dev = strtoul(maj_dev_start, NULL, 16);
453
454
    if (mapping_name == 0)
455
  {
456
      while (*p && *p++ != '\n');
457
    } else {
458
      while (*p && *p != '\n' && *p != '/' && *p != '[')
459
      p++;
460
      *mapping_name = p;
461
      while (*p && *p++ != '\n');
462
    }
463
464
    return p;
465
}
466
467
#define MIN_PAGE_SIZE (256)
468
469
static jmp_buf case_jmp_buf;
470
471
static void case_fault_handler(int sig)
472
{
473
  longjmp(case_jmp_buf, 1);
474
}
475
476
static struct sigaction old_segv_act;
477
static struct sigaction old_bus_act;
478
479
static void case_setup_temporary_fault_handler(void)
480
{
481
  struct sigaction act;
482
  act.sa_handler = case_fault_handler;
483
  act.sa_flags = SA_RESTART;
484
  (void)sigemptyset(&act.sa_mask);
485
  (void)sigaction(SIGSEGV, &act, &old_segv_act);
486
  (void)sigaction(SIGBUS, &act, &old_bus_act);
487
}
488
489
static void case_reset_fault_handler(void)
490
{
491
  (void)sigaction(SIGSEGV, &old_segv_act, 0);
492
  (void)sigaction(SIGBUS, &old_bus_act, 0);
493
}
494
495
static void case_find_limit(char* p, void** start, void** end)
496
{
497
  static volatile char* result;
498
  static volatile char sink;
499
  case_setup_temporary_fault_handler();
500
  if (setjmp(case_jmp_buf) == 0)
501
  {
502
    result = (char*)((uint64_t)p & (uint64_t)~(MIN_PAGE_SIZE - 1));
503
    for (;;)
504
    {
505
      result -= MIN_PAGE_SIZE;
506
      sink = *result;
507
    }
508
  }
509
  *start = (void*)(result + MIN_PAGE_SIZE);
510
  if (setjmp(case_jmp_buf) == 0)
511
  {
512
    result = (char*)((uint64_t)p & (uint64_t)~(MIN_PAGE_SIZE - 1));
513
    for (;;)
514
    {
515
      result += MIN_PAGE_SIZE;
516
      sink = *result;
517
    }
518
  }
519
  *end = (void*)result;
520
  case_reset_fault_handler();
521
}
522
523
static char _test_end[8];
524
525
int main(int argc, char** argv)
526
{
527
  const char* match_test = (argc == 2) ? argv[1] : 0;
528
  char* buf = case_get_maps();
529
  void* start;
530
  void* end;
531
  char* prot[4];
532
  unsigned int maj_dev;
533
  static uint64_t the_sig = 0x883253372849284B;
534
  if (buf == 0)
535
  {
536
    case_find_limit(_test_end, &start, &end);
537
  } else {
538
    do {
539
      buf = case_parse_map_entry(buf, &start, &end, (char**)&prot, &maj_dev, 0);
540
      if (buf == NULL)
541
        break;
542
    } while ((intptr_t)start >= (intptr_t)&_test_end || (intptr_t)&_test_end >= (intptr_t)end);
543
  }
544
  char* start_pointer = (char*)start;
545
  int total = 0;
546
  int len = (intptr_t)end - (intptr_t)start - sizeof(case_t) + 1;
547
  int i;
548
  for (i = 0; i < len; i++)
549
  {
550
    case_t* test_case = (case_t*)(start_pointer + i);
551
    if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
552
      (!match_test || strstr(test_case->name, match_test)))
553
      total++;
554
  }
555
  char dir_buf[1024];
556
  char* cur_dir = getcwd(dir_buf, 1024);
557
  if (__test_case_setup)
558
    __test_case_setup();
559
  int j = 0, pass = 0, skip = 0, fail = 0;
560
  for (i = 0; i < len; i++)
561
  {
562
    case_t* test_case = (case_t*)(start_pointer + i);
563
    if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
564
      (!match_test || strstr(test_case->name, match_test)))
565
    {
566
      case_run(test_case, match_test, j++, total, &pass, &skip, &fail);
567
      chdir(cur_dir);
568
    }
569
  }
570
  if (__test_case_teardown)
571
    __test_case_teardown();
572
  case_conclude(pass, skip, fail);
573
  return fail;
574
}
575
576
#endif
577
#endif