cpp.cc raw

   1  /*
   2   * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
   3   *
   4   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   5   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   6   *
   7   * Permission is hereby granted to use or copy this program
   8   * for any purpose, provided the above notices are retained on all copies.
   9   * Permission to modify the code and to distribute modified code is granted,
  10   * provided the above notices are retained, and a notice that the code was
  11   * modified is included with the above copyright notice.
  12   */
  13  
  14  // This program tries to test the specific C++ functionality provided by
  15  // `gc_cpp.h` file that is not tested by the more general test routines
  16  // of the collector.
  17  
  18  #ifdef HAVE_CONFIG_H
  19  #  include "config.h"
  20  #endif
  21  
  22  #undef GC_BUILD
  23  
  24  #define GC_DONT_INCL_WINDOWS_H
  25  #include "gc_cpp.h"
  26  
  27  #include <stdlib.h>
  28  #include <string.h>
  29  
  30  #define GC_NAMESPACE_ALLOCATOR
  31  #include "gc/gc_allocator.h"
  32  using boehmgc::gc_allocator;
  33  using boehmgc::gc_allocator_ignore_off_page;
  34  using boehmgc::traceable_allocator;
  35  
  36  #include "private/gcconfig.h"
  37  
  38  #ifndef GC_API_PRIV
  39  #  define GC_API_PRIV GC_API
  40  #endif
  41  extern "C" {
  42  // Use the collector private output to reach the same log file.
  43  // Do not include `gc_priv.h` file, since that may include Windows
  44  // system header files that do not take kindly to this context.
  45  GC_API_PRIV void GC_printf(const char *format, ...);
  46  }
  47  
  48  #ifdef MSWIN32
  49  #  ifndef WIN32_LEAN_AND_MEAN
  50  #    define WIN32_LEAN_AND_MEAN 1
  51  #  endif
  52  #  define NOSERVICE
  53  #  include <windows.h>
  54  #endif
  55  
  56  #ifdef GC_NAME_CONFLICT
  57  #  define USE_GC GC_NS_QUALIFY(UseGC)
  58  struct foo *GC;
  59  #else
  60  #  define USE_GC GC_NS_QUALIFY(GC)
  61  #endif
  62  
  63  #if __cplusplus >= 201103L
  64  #  define GC_OVERRIDE override
  65  #else
  66  #  define GC_OVERRIDE /*< empty */
  67  #endif
  68  
  69  #define my_assert(e)                                                  \
  70    if (!(e)) {                                                         \
  71      GC_printf("Assertion failure in " __FILE__ ", line %d: " #e "\n", \
  72                __LINE__);                                              \
  73      exit(1);                                                          \
  74    }
  75  
  76  #if defined(__powerpc64__) && !defined(__clang__) && GC_GNUC_PREREQ(10, 0)
  77  // Suppress "layout of aggregates ... has changed" GCC note.
  78  #  define A_I_TYPE short
  79  #else
  80  #  define A_I_TYPE int
  81  #endif
  82  
  83  #define LARGE_CPP_ITER_CNT 1000000
  84  
  85  // An uncollectible class.
  86  class A
  87  {
  88  public:
  89    GC_ATTR_EXPLICIT
  90    A(int iArg) : i(static_cast<A_I_TYPE>(iArg)) {}
  91    void
  92    Test(int iArg)
  93    {
  94      my_assert(i == iArg);
  95    }
  96    virtual ~A() {}
  97    A_I_TYPE i;
  98  };
  99  
 100  // A collectible class.
 101  class B : public GC_NS_QUALIFY(gc), public A
 102  {
 103  public:
 104    GC_ATTR_EXPLICIT
 105    B(int j) : A(j) {}
 106    virtual ~B() GC_OVERRIDE { my_assert(deleting); }
 107    static void
 108    Deleting(int on)
 109    {
 110      deleting = on;
 111    }
 112    static int deleting;
 113  };
 114  
 115  int B::deleting = 0;
 116  
 117  #define C_INIT_LEFT_RIGHT(arg_l, arg_r) \
 118    {                                     \
 119      C *l = new C(arg_l);                \
 120      C *r = new C(arg_r);                \
 121      left = l;                           \
 122      right = r;                          \
 123      if (GC_is_heap_ptr(this)) {         \
 124        GC_END_STUBBORN_CHANGE(this);     \
 125        GC_reachable_here(l);             \
 126        GC_reachable_here(r);             \
 127      }                                   \
 128    }
 129  
 130  // A collectible class with cleanup and virtual multiple inheritance.
 131  class C : public GC_NS_QUALIFY(gc_cleanup), public A
 132  {
 133  public:
 134    // The class uses dynamic memory/resource allocation, so provide both
 135    // a copy constructor and an assignment operator to workaround a cppcheck
 136    // warning.
 137    C(const C &c) : A(c.i), level(c.level), left(0), right(0)
 138    {
 139      if (level > 0)
 140        C_INIT_LEFT_RIGHT(*c.left, *c.right);
 141    }
 142  
 143    C &
 144    operator=(const C &c)
 145    {
 146      if (this != &c) {
 147        delete left;
 148        delete right;
 149        i = c.i;
 150        level = c.level;
 151        left = 0;
 152        right = 0;
 153        if (level > 0)
 154          C_INIT_LEFT_RIGHT(*c.left, *c.right);
 155      }
 156      return *this;
 157    }
 158  
 159    GC_ATTR_EXPLICIT
 160    C(int levelArg) : A(levelArg), level(levelArg)
 161    {
 162      nAllocated++;
 163      if (level > 0) {
 164        C_INIT_LEFT_RIGHT(level - 1, level - 1);
 165      } else {
 166        left = right = 0;
 167      }
 168    }
 169    ~C() GC_OVERRIDE
 170    {
 171      this->A::Test(level);
 172      nFreed++;
 173      my_assert(level == 0
 174                    ? left == 0 && right == 0
 175                    : level == left->level + 1 && level == right->level + 1);
 176      left = right = 0;
 177      level = -32456;
 178    }
 179    static void
 180    Test()
 181    {
 182      if (GC_is_incremental_mode() && nFreed < (nAllocated / 5) * 4) {
 183        // An explicit garbage collection might be needed to reach the expected
 184        // number of the finalized objects.
 185        GC_gcollect();
 186      }
 187      my_assert(nFreed <= nAllocated);
 188  #ifndef GC_NO_FINALIZATION
 189      my_assert(nFreed >= (nAllocated / 5) * 4 || GC_get_find_leak());
 190  #endif
 191    }
 192  
 193    static int nFreed;
 194    static int nAllocated;
 195    int level;
 196    C *left;
 197    C *right;
 198  };
 199  
 200  int C::nFreed = 0;
 201  int C::nAllocated = 0;
 202  
 203  // A collectible class with a static member function to be used as
 204  // an explicit cleanup function supplied to operator `::new`.
 205  class D : public GC_NS_QUALIFY(gc)
 206  {
 207  public:
 208    GC_ATTR_EXPLICIT
 209    D(int iArg) : i(iArg) { nAllocated++; }
 210    static void
 211    CleanUp(void *obj, void *data)
 212    {
 213      const D *self = static_cast<D *>(obj);
 214      nFreed++;
 215      my_assert(static_cast<GC_uintptr_t>(self->i)
 216                == reinterpret_cast<GC_uintptr_t>(data));
 217    }
 218    static void
 219    Test()
 220    {
 221  #ifndef GC_NO_FINALIZATION
 222      my_assert(nFreed >= (nAllocated / 5) * 4 || GC_get_find_leak());
 223  #endif
 224    }
 225  
 226    int i;
 227    static int nFreed;
 228    static int nAllocated;
 229  };
 230  
 231  int D::nFreed = 0;
 232  int D::nAllocated = 0;
 233  
 234  // A collectible class with cleanup for use by `F`.
 235  class E : public GC_NS_QUALIFY(gc_cleanup)
 236  {
 237  public:
 238    E() { nAllocated++; }
 239    ~E() GC_OVERRIDE { nFreed++; }
 240  
 241    static int nFreed;
 242    static int nAllocated;
 243  };
 244  
 245  int E::nFreed = 0;
 246  int E::nAllocated = 0;
 247  
 248  // A collectible class with cleanup, a base with cleanup, and
 249  // a member with cleanup.
 250  class F : public E
 251  {
 252  public:
 253    F() { nAllocatedF++; }
 254  
 255    ~F() GC_OVERRIDE { nFreedF++; }
 256  
 257    static void
 258    Test()
 259    {
 260  #ifndef GC_NO_FINALIZATION
 261      my_assert(nFreedF >= (nAllocatedF / 5) * 4 || GC_get_find_leak());
 262  #endif
 263      my_assert(2 * nFreedF == nFreed);
 264    }
 265  
 266    E e;
 267    static int nFreedF;
 268    static int nAllocatedF;
 269  };
 270  
 271  int F::nFreedF = 0;
 272  int F::nAllocatedF = 0;
 273  
 274  GC_uintptr_t
 275  Disguise(void *p)
 276  {
 277    return GC_HIDE_NZ_POINTER(p);
 278  }
 279  
 280  void *
 281  Undisguise(GC_uintptr_t v)
 282  {
 283    return GC_REVEAL_NZ_POINTER(v);
 284  }
 285  
 286  // Note: `delete p` should invoke `GC_FREE()`.
 287  #define GC_CHECKED_DELETE(p)                                  \
 288    {                                                           \
 289      size_t freed_before = GC_get_expl_freed_bytes_since_gc(); \
 290      delete p;                                                 \
 291      size_t freed_after = GC_get_expl_freed_bytes_since_gc();  \
 292      my_assert(freed_before != freed_after);                   \
 293    }
 294  
 295  #define N_TESTS 7
 296  
 297  #if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \
 298      && !defined(NO_WINMAIN_ENTRY)
 299  int APIENTRY
 300  WinMain(HINSTANCE /* `instance` */, HINSTANCE /* `prev` */, LPSTR cmd,
 301          int /* `cmdShow` */)
 302  {
 303    int argc = 0;
 304    char *argv[3];
 305  
 306  #  if defined(CPPCHECK)
 307    GC_noop1(static_cast<GC_word>(reinterpret_cast<GC_uintptr_t>(&WinMain)));
 308  #  endif
 309    if (cmd != 0)
 310      for (argc = 1; argc < static_cast<int>(sizeof(argv) / sizeof(argv[0]));
 311           argc++) {
 312        // Parse the command-line string.  Non-reentrant `strtok()` is not used
 313        // to avoid complains of static analysis tools.  (And, `strtok_r()` is
 314        // not available on some platforms.)  The code is equivalent to:
 315        // `if (!(argv[argc] = strtok(argc == 1 ? cmd : 0, " \t"))) break;`.
 316        if (NULL == cmd) {
 317          argv[argc] = NULL;
 318          break;
 319        }
 320        for (; *cmd != '\0'; cmd++) {
 321          if (*cmd != ' ' && *cmd != '\t')
 322            break;
 323        }
 324        if ('\0' == *cmd) {
 325          argv[argc] = NULL;
 326          break;
 327        }
 328        argv[argc] = cmd;
 329        while (*(++cmd) != '\0') {
 330          if (*cmd == ' ' || *cmd == '\t')
 331            break;
 332        }
 333        if (*cmd != '\0') {
 334          *(cmd++) = '\0';
 335        } else {
 336          cmd = NULL;
 337        }
 338      }
 339  #else
 340  int
 341  main(int argc, const char *argv[])
 342  {
 343  #endif
 344  
 345    // This is needed due to C++ multiple inheritance used.
 346    GC_set_all_interior_pointers(1);
 347  
 348  #ifdef TEST_MANUAL_VDB
 349    GC_set_manual_vdb_allowed(1);
 350  #endif
 351  #if !defined(CPPCHECK)
 352    GC_INIT();
 353  #endif
 354  #ifndef NO_INCREMENTAL
 355    GC_enable_incremental();
 356  #endif
 357    if (GC_get_find_leak())
 358      GC_printf("This test program is not designed for leak detection mode\n");
 359  
 360    int i, iters, n;
 361    int *x = gc_allocator<int>().allocate(1);
 362    const int *xio;
 363    xio = gc_allocator_ignore_off_page<int>().allocate(1);
 364    GC_reachable_here(xio);
 365    int **xptr = traceable_allocator<int *>().allocate(1);
 366    *x = 29;
 367    GC_PTR_STORE_AND_DIRTY(xptr, x);
 368    x = 0;
 369    if (argc != 2 || (n = atoi(argv[1])) <= 0) {
 370      GC_printf("usage: cpptest <number-of-iterations>\n"
 371                "Assuming %d iterations\n",
 372                N_TESTS);
 373      n = N_TESTS;
 374    }
 375  #ifdef LINT2
 376    if (n > 30 * 1000)
 377      n = 30 * 1000;
 378  #endif
 379  
 380    for (iters = 1; iters <= n; iters++) {
 381      GC_printf("Starting iteration %d\n", iters);
 382  
 383      // Allocate some uncollectible objects and disguise their pointers.
 384      // Later we will check to see if the objects are still there.
 385      // We are checking to make sure these objects are uncollectible really.
 386      GC_uintptr_t as[1000];
 387      GC_uintptr_t bs[1000];
 388      for (i = 0; i < 1000; i++) {
 389        as[i] = Disguise(new (GC_NS_QUALIFY(NoGC)) A(i));
 390        bs[i] = Disguise(new (GC_NS_QUALIFY(NoGC)) B(i));
 391      }
 392  
 393      // Allocate a fair number of finalizable objects.
 394      // Later we will check to make sure they have gone away.
 395      for (i = 0; i < 1000; i++) {
 396        C *c = new C(2);
 397        // Stack allocation should work too.
 398        C c1(2);
 399        F *f;
 400  #if !defined(CPPCHECK)
 401        D *d;
 402        d = ::new (USE_GC, D::CleanUp,
 403                   reinterpret_cast<void *>(static_cast<GC_uintptr_t>(i))) D(i);
 404        GC_reachable_here(d);
 405  #endif
 406        f = new F;
 407        F **fa = new F *[1];
 408        fa[0] = f;
 409        (void)fa;
 410        delete[] fa;
 411        if (0 == i % 10)
 412          GC_CHECKED_DELETE(c);
 413      }
 414  
 415      // Allocate a very large number of collectible objects and drop
 416      // the references to them immediately, forcing many collections.
 417      for (i = 0; i < LARGE_CPP_ITER_CNT; i++) {
 418        const A *a;
 419        a = new (USE_GC) A(i);
 420        GC_reachable_here(a);
 421        B *b;
 422        b = new B(i);
 423        (void)b;
 424        b = new (USE_GC) B(i);
 425        if (0 == i % 10) {
 426          B::Deleting(1);
 427          GC_CHECKED_DELETE(b);
 428          B::Deleting(0);
 429        }
 430  #if defined(FINALIZE_ON_DEMAND) && !defined(GC_NO_FINALIZATION)
 431        GC_invoke_finalizers();
 432  #endif
 433      }
 434  
 435      // Make sure the uncollectible objects are still there.
 436      for (i = 0; i < 1000; i++) {
 437        A *a = static_cast<A *>(Undisguise(as[i]));
 438        B *b = static_cast<B *>(Undisguise(bs[i]));
 439        a->Test(i);
 440  #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
 441        // Workaround for ASan/MSan: the linker uses operator `delete`
 442        // implementation from `libclang_rt` instead of `gccpp` library (thus
 443        // causing incompatible `alloc`/`free`).
 444        GC_FREE(a);
 445  #else
 446        GC_CHECKED_DELETE(a);
 447  #endif
 448        b->Test(i);
 449        B::Deleting(1);
 450        GC_CHECKED_DELETE(b);
 451        B::Deleting(0);
 452  #if defined(FINALIZE_ON_DEMAND) && !defined(GC_NO_FINALIZATION)
 453        GC_invoke_finalizers();
 454  #endif
 455      }
 456  
 457      // Make sure most of the finalizable objects have gone away.
 458      C::Test();
 459      D::Test();
 460      F::Test();
 461    }
 462  
 463    x = *xptr;
 464    my_assert(29 == x[0]);
 465    GC_printf("The test appears to have succeeded.\n");
 466    return 0;
 467  }
 468