tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SmokeDMD.cpp (10554B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // This program is used by the DMD xpcshell test. It is run under DMD and
      8 // produces some output. The xpcshell test then post-processes and checks this
      9 // output.
     10 //
     11 // Note that this file does not have "Test" or "test" in its name, because that
     12 // will cause the build system to not record breakpad symbols for it, which
     13 // will stop the post-processing (which includes stack fixing) from working
     14 // correctly.
     15 
     16 // This is required on some systems such as Fedora to allow
     17 // building with -O0 together with --warnings-as-errors due to
     18 // a check in /usr/include/features.h
     19 #undef _FORTIFY_SOURCE
     20 
     21 #include <errno.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 
     25 #include "mozilla/Assertions.h"
     26 #include "mozilla/JSONWriter.h"
     27 #include "mozilla/Sprintf.h"
     28 #include "mozilla/UniquePtr.h"
     29 #include "DMD.h"
     30 
     31 using mozilla::MakeUnique;
     32 using namespace mozilla::dmd;
     33 
     34 MOZ_RUNINIT DMDFuncs::Singleton DMDFuncs::sSingleton;
     35 
     36 class FpWriteFunc final : public mozilla::JSONWriteFunc {
     37 public:
     38  explicit FpWriteFunc(const char* aFilename) {
     39    mFp = fopen(aFilename, "w");
     40    if (!mFp) {
     41      fprintf(stderr, "SmokeDMD: can't create %s file: %s\n", aFilename,
     42              strerror(errno));
     43      exit(1);
     44    }
     45  }
     46 
     47  ~FpWriteFunc() { fclose(mFp); }
     48 
     49  void Write(const mozilla::Span<const char>& aStr) final {
     50    for (const char c : aStr) {
     51      fputc(c, mFp);
     52    }
     53  }
     54 
     55 private:
     56  FILE* mFp;
     57 };
     58 
     59 // This stops otherwise-unused variables from being optimized away.
     60 static void UseItOrLoseIt(void* aPtr, int aSeven) {
     61  char buf[64];
     62  int n = SprintfLiteral(buf, "%p\n", aPtr);
     63  if (n == 20 + aSeven) {
     64    fprintf(stderr, "well, that is surprising");
     65  }
     66 }
     67 
     68 // This function checks that heap blocks that have the same stack trace but
     69 // different (or no) reporters get aggregated separately.
     70 void Foo(int aSeven) {
     71  char* a[6];
     72  for (int i = 0; i < aSeven - 1; i++) {
     73    a[i] = (char*)malloc(128 - 16 * i);
     74    UseItOrLoseIt(a[i], aSeven);
     75  }
     76 
     77  // Oddly, some versions of clang will cause identical stack traces to be
     78  // generated for adjacent calls to Report(), which breaks the test. Inserting
     79  // the UseItOrLoseIt() calls in between is enough to prevent this.
     80 
     81  Report(a[2]);  // reported
     82 
     83  UseItOrLoseIt(a[2], aSeven);
     84 
     85  for (int i = 0; i < aSeven - 5; i++) {
     86    Report(a[i]);  // reported
     87    UseItOrLoseIt(a[i], aSeven);
     88  }
     89 
     90  UseItOrLoseIt(a[2], aSeven);
     91 
     92  Report(a[3]);  // reported
     93 
     94  // a[4], a[5] unreported
     95 }
     96 
     97 void TestEmpty(const char* aTestName, const char* aMode) {
     98  char filename[128];
     99  SprintfLiteral(filename, "complete-%s-%s.json", aTestName, aMode);
    100  auto f = MakeUnique<FpWriteFunc>(filename);
    101 
    102  char options[128];
    103  SprintfLiteral(options, "--mode=%s --stacks=full", aMode);
    104  ResetEverything(options);
    105 
    106  // Zero for everything.
    107  Analyze(std::move(f));
    108 }
    109 
    110 void TestFull(const char* aTestName, int aNum, const char* aMode, int aSeven) {
    111  char filename[128];
    112  SprintfLiteral(filename, "complete-%s%d-%s.json", aTestName, aNum, aMode);
    113  auto f = MakeUnique<FpWriteFunc>(filename);
    114 
    115  // The --show-dump-stats=yes is there just to give that option some basic
    116  // testing, e.g. ensure it doesn't crash. It's hard to test much beyond that.
    117  char options[128];
    118  SprintfLiteral(options, "--mode=%s --stacks=full --show-dump-stats=yes",
    119                 aMode);
    120  ResetEverything(options);
    121 
    122  // Analyze 1: 1 freed, 9 out of 10 unreported.
    123  // Analyze 2: still present and unreported.
    124  int i;
    125  char* a = nullptr;
    126  for (i = 0; i < aSeven + 3; i++) {
    127    a = (char*)malloc(100);
    128    UseItOrLoseIt(a, aSeven);
    129  }
    130  free(a);
    131 
    132  // A no-op.
    133  free(nullptr);
    134 
    135  // Note: 16 bytes is the smallest requested size that gives consistent
    136  // behaviour across all platforms with jemalloc.
    137  // Analyze 1: reported.
    138  // Analyze 2: thrice-reported.
    139  char* a2 = (char*)malloc(16);
    140  Report(a2);
    141 
    142  // Analyze 1: reported.
    143  // Analyze 2: reportedness carries over, due to ReportOnAlloc.
    144  char* b = (char*)malloc(10);
    145  ReportOnAlloc(b);
    146 
    147  // ReportOnAlloc, then freed.
    148  // Analyze 1: freed, irrelevant.
    149  // Analyze 2: freed, irrelevant.
    150  char* b2 = (char*)malloc(16);
    151  ReportOnAlloc(b2);
    152  free(b2);
    153 
    154  // Analyze 1: reported 4 times.
    155  // Analyze 2: freed, irrelevant.
    156  char* c = (char*)calloc(10, 3);
    157  Report(c);
    158  for (int i = 0; i < aSeven - 4; i++) {
    159    Report(c);
    160  }
    161 
    162  // Analyze 1: ignored.
    163  // Analyze 2: irrelevant.
    164  Report((void*)(intptr_t)i);
    165 
    166  // jemalloc rounds this up to 8192.
    167  // Analyze 1: reported.
    168  // Analyze 2: freed.
    169  char* e = (char*)malloc(4096);
    170  e = (char*)realloc(e, 7169);
    171  Report(e);
    172 
    173  // First realloc is like malloc;  second realloc is shrinking.
    174  // Analyze 1: reported.
    175  // Analyze 2: re-reported.
    176  char* e2 = (char*)realloc(nullptr, 1024);
    177  e2 = (char*)realloc(e2, 512);
    178  Report(e2);
    179 
    180  // First realloc is like malloc;  second realloc creates a min-sized block.
    181  // XXX: on Windows, second realloc frees the block.
    182  // Analyze 1: reported.
    183  // Analyze 2: freed, irrelevant.
    184  char* e3 = (char*)realloc(nullptr, 1023);
    185  // e3 = (char*) realloc(e3, 0);
    186  MOZ_ASSERT(e3);
    187  Report(e3);
    188 
    189  // Analyze 1: freed, irrelevant.
    190  // Analyze 2: freed, irrelevant.
    191  char* f1 = (char*)malloc(64);
    192  UseItOrLoseIt(f1, aSeven);
    193  free(f1);
    194 
    195  // Analyze 1: ignored.
    196  // Analyze 2: irrelevant.
    197  Report((void*)(intptr_t)0x0);
    198 
    199  // Analyze 1: mixture of reported and unreported.
    200  // Analyze 2: all unreported.
    201  Foo(aSeven);
    202 
    203  // Analyze 1: twice-reported.
    204  // Analyze 2: twice-reported.
    205  char* g1 = (char*)malloc(77);
    206  ReportOnAlloc(g1);
    207  ReportOnAlloc(g1);
    208 
    209  // Analyze 1: mixture of reported and unreported.
    210  // Analyze 2: all unreported.
    211  // Nb: this Foo() call is deliberately not adjacent to the previous one. See
    212  // the comment about adjacent calls in Foo() for more details.
    213  Foo(aSeven);
    214 
    215  // Analyze 1: twice-reported.
    216  // Analyze 2: once-reported.
    217  char* g2 = (char*)malloc(78);
    218  Report(g2);
    219  ReportOnAlloc(g2);
    220 
    221  // Analyze 1: twice-reported.
    222  // Analyze 2: once-reported.
    223  char* g3 = (char*)malloc(79);
    224  ReportOnAlloc(g3);
    225  Report(g3);
    226 
    227  // All the odd-ball ones.
    228  // Analyze 1: all unreported.
    229  // Analyze 2: all freed, irrelevant.
    230  // XXX: no memalign on Mac
    231  // void* w = memalign(64, 65);           // rounds up to 128
    232  // UseItOrLoseIt(w, aSeven);
    233 
    234  // XXX: posix_memalign doesn't work on B2G
    235  // void* x;
    236  // posix_memalign(&y, 128, 129);         // rounds up to 256
    237  // UseItOrLoseIt(x, aSeven);
    238 
    239  // XXX: valloc doesn't work on Windows.
    240  // void* y = valloc(1);                  // rounds up to 4096
    241  // UseItOrLoseIt(y, aSeven);
    242 
    243  // XXX: C11 only
    244  // void* z = aligned_alloc(64, 256);
    245  // UseItOrLoseIt(z, aSeven);
    246 
    247  if (aNum == 1) {
    248    // Analyze 1.
    249    Analyze(std::move(f));
    250  }
    251 
    252  ClearReports();
    253 
    254  //---------
    255 
    256  Report(a2);
    257  Report(a2);
    258  free(c);
    259  free(e);
    260  Report(e2);
    261  free(e3);
    262  // free(w);
    263  // free(x);
    264  // free(y);
    265  // free(z);
    266 
    267  // Do some allocations that will only show up in cumulative mode.
    268  for (int i = 0; i < 100; i++) {
    269    void* v = malloc(128);
    270    UseItOrLoseIt(v, aSeven);
    271    free(v);
    272  }
    273 
    274  if (aNum == 2) {
    275    // Analyze 2.
    276    Analyze(std::move(f));
    277  }
    278 }
    279 
    280 void TestPartial(const char* aTestName, const char* aMode, int aSeven) {
    281  char filename[128];
    282  SprintfLiteral(filename, "complete-%s-%s.json", aTestName, aMode);
    283  auto f = MakeUnique<FpWriteFunc>(filename);
    284 
    285  char options[128];
    286  SprintfLiteral(options, "--mode=%s", aMode);
    287  ResetEverything(options);
    288 
    289  int kTenThousand = aSeven + 9993;
    290  char* s;
    291 
    292  // The output of this function is deterministic but it relies on the
    293  // probability and seeds given to the FastBernoulliTrial instance in
    294  // ResetBernoulli(). If they change, the output will change too.
    295 
    296  // Expected fraction with stacks: (1 - (1 - 0.003) ** 16) = 0.0469.
    297  // So we expect about 0.0469 * 10000 == 469.
    298  // We actually get 511.
    299  for (int i = 0; i < kTenThousand; i++) {
    300    s = (char*)malloc(16);
    301    UseItOrLoseIt(s, aSeven);
    302  }
    303 
    304  // Expected fraction with stacks: (1 - (1 - 0.003) ** 128) = 0.3193.
    305  // So we expect about 0.3193 * 10000 == 3193.
    306  // We actually get 3136.
    307  for (int i = 0; i < kTenThousand; i++) {
    308    s = (char*)malloc(128);
    309    UseItOrLoseIt(s, aSeven);
    310  }
    311 
    312  // Expected fraction with stacks: (1 - (1 - 0.003) ** 1024) = 0.9539.
    313  // So we expect about 0.9539 * 10000 == 9539.
    314  // We actually get 9531.
    315  for (int i = 0; i < kTenThousand; i++) {
    316    s = (char*)malloc(1024);
    317    UseItOrLoseIt(s, aSeven);
    318  }
    319 
    320  Analyze(std::move(f));
    321 }
    322 
    323 void TestScan(int aSeven) {
    324  auto f = MakeUnique<FpWriteFunc>("basic-scan.json");
    325 
    326  ResetEverything("--mode=scan");
    327 
    328  uintptr_t* p = (uintptr_t*)malloc(6 * sizeof(uintptr_t));
    329  UseItOrLoseIt(p, aSeven);
    330 
    331  // Hard-coded values checked by scan-test.py
    332  p[0] = 0x123;                         // outside a block, small value
    333  p[1] = 0x0;                           // null
    334  p[2] = (uintptr_t)((uint8_t*)p - 1);  // pointer outside a block, but nearby
    335  p[3] = (uintptr_t)p;                  // pointer to start of a block
    336  p[4] = (uintptr_t)((uint8_t*)p + 1);  // pointer into a block
    337  p[5] = 0x0;                           // trailing null
    338 
    339  Analyze(std::move(f));
    340 }
    341 
    342 void RunTests() {
    343  // This test relies on the compiler not doing various optimizations, such as
    344  // eliding unused malloc() calls or unrolling loops with fixed iteration
    345  // counts. So we compile it with -O0 (or equivalent), which probably prevents
    346  // that. We also use the following variable for various loop iteration
    347  // counts, just in case compilers might unroll very small loops even with
    348  // -O0.
    349  int seven = 7;
    350 
    351  // Make sure that DMD is actually running; it is initialized on the first
    352  // allocation.
    353  int* x = (int*)malloc(100);
    354  UseItOrLoseIt(x, seven);
    355  MOZ_RELEASE_ASSERT(IsRunning());
    356 
    357  // Please keep this in sync with run_test in test_dmd.js.
    358 
    359  TestEmpty("empty", "live");
    360  TestEmpty("empty", "dark-matter");
    361  TestEmpty("empty", "cumulative");
    362 
    363  TestFull("full", 1, "live", seven);
    364  TestFull("full", 1, "dark-matter", seven);
    365 
    366  TestFull("full", 2, "dark-matter", seven);
    367  TestFull("full", 2, "cumulative", seven);
    368 
    369  TestPartial("partial", "live", seven);
    370 
    371  TestScan(seven);
    372 }
    373 
    374 int main() {
    375  RunTests();
    376 
    377  return 0;
    378 }