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