|
10 | 10 | // This test mostly focuses on edge cases. But more coverage of normal
|
11 | 11 | // operations wouldn't be a bad thing.
|
12 | 12 |
|
| 13 | +#ifdef XP_UNIX |
| 14 | +#include <unistd.h> |
| 15 | +#include <sys/types.h> |
| 16 | +#include <sys/wait.h> |
| 17 | + |
| 18 | +// This global variable is defined in toolkit/xre/nsSigHandlers.cpp. |
| 19 | +extern unsigned int _gdb_sleep_duration; |
| 20 | +#endif |
| 21 | + |
| 22 | +// We can test that certain operations cause expected aborts by forking |
| 23 | +// and then checking that the child aborted in the expected way (i.e. via |
| 24 | +// MOZ_CRASH). We skip this for the following configurations. |
| 25 | +// - On Windows, because it doesn't have fork(). |
| 26 | +// - On non-DEBUG builds, because the crashes cause the crash reporter to pop |
| 27 | +// up when running this test locally, which is surprising and annoying. |
| 28 | +// - On ASAN builds, because ASAN alters the way a MOZ_CRASHing process |
| 29 | +// terminates, which makes it harder to test if the right thing has occurred. |
| 30 | +void |
| 31 | +TestCrashyOperation(void (*aCrashyOperation)()) |
| 32 | +{ |
| 33 | +#if defined(XP_UNIX) && defined(DEBUG) && !defined(MOZ_ASAN) |
| 34 | + // We're about to trigger a crash. When it happens don't pause to allow GDB |
| 35 | + // to be attached. |
| 36 | + unsigned int old_gdb_sleep_duration = _gdb_sleep_duration; |
| 37 | + _gdb_sleep_duration = 0; |
| 38 | + |
| 39 | + int pid = fork(); |
| 40 | + ASSERT_NE(pid, -1); |
| 41 | + |
| 42 | + if (pid == 0) { |
| 43 | + // Child: perform the crashy operation. |
| 44 | + aCrashyOperation(); |
| 45 | + fprintf(stderr, "TestCrashyOperation: didn't crash?!\n"); |
| 46 | + ASSERT_TRUE(false); // shouldn't reach here |
| 47 | + } |
| 48 | + |
| 49 | + // Parent: check that child crashed as expected. |
| 50 | + int status; |
| 51 | + ASSERT_NE(waitpid(pid, &status, 0), -1); |
| 52 | + |
| 53 | + // The path taken here depends on the platform and configuration. |
| 54 | + ASSERT_TRUE(WIFEXITED(status) || WTERMSIG(status)); |
| 55 | + if (WIFEXITED(status)) { |
| 56 | + // This occurs if the ah_crap_handler() is run, i.e. we caught the crash. |
| 57 | + // It returns the number of the caught signal. |
| 58 | + int signum = WEXITSTATUS(status); |
| 59 | + if (signum != SIGSEGV && signum != SIGBUS) { |
| 60 | + fprintf(stderr, "TestCrashyOperation 'exited' failure: %d\n", signum); |
| 61 | + ASSERT_TRUE(false); |
| 62 | + } |
| 63 | + } else if (WIFSIGNALED(status)) { |
| 64 | + // This one occurs if we didn't catch the crash. The exit code is the |
| 65 | + // number of the terminating signal. |
| 66 | + int signum = WTERMSIG(status); |
| 67 | + if (signum != SIGSEGV && signum != SIGBUS) { |
| 68 | + fprintf(stderr, "TestCrashyOperation 'signaled' failure: %d\n", signum); |
| 69 | + ASSERT_TRUE(false); |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + _gdb_sleep_duration = old_gdb_sleep_duration; |
| 74 | +#endif |
| 75 | +} |
| 76 | + |
| 77 | +void |
| 78 | +InitCapacityOk_InitialLengthTooBig() |
| 79 | +{ |
| 80 | + PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub), |
| 81 | + PL_DHASH_MAX_INITIAL_LENGTH + 1); |
| 82 | +} |
| 83 | + |
| 84 | +void |
| 85 | +InitCapacityOk_InitialEntryStoreTooBig() |
| 86 | +{ |
| 87 | + // Try the smallest disallowed power-of-two entry store size, which is 2^32 |
| 88 | + // bytes (which overflows to 0). (Note that the 2^23 *length* gets converted |
| 89 | + // to a 2^24 *capacity*.) |
| 90 | + PLDHashTable t(PL_DHashGetStubOps(), (uint32_t)1 << 23, (uint32_t)1 << 8); |
| 91 | +} |
| 92 | + |
13 | 93 | TEST(PLDHashTableTest, InitCapacityOk)
|
14 | 94 | {
|
15 | 95 | // Try the largest allowed capacity. With PL_DHASH_MAX_CAPACITY==1<<26, this
|
16 | 96 | // would allocate (if we added an element) 0.5GB of entry store on 32-bit
|
17 | 97 | // platforms and 1GB on 64-bit platforms.
|
18 |
| - // |
19 |
| - // Ideally we'd also try (a) a too-large capacity, and (b) a large capacity |
20 |
| - // combined with a large entry size that when multipled overflow. But those |
21 |
| - // cases would cause the test to abort immediately. |
22 |
| - // |
23 |
| - // Furthermore, ideally we'd also try a large-but-ok capacity that almost but |
24 |
| - // doesn't quite overflow, but that would result in allocating just under 4GB |
25 |
| - // of entry storage. That's very likely to fail on 32-bit platforms, so such |
26 |
| - // a test wouldn't be reliable. |
27 |
| - // |
28 |
| - PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub), |
| 98 | + PLDHashTable t1(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub), |
29 | 99 | PL_DHASH_MAX_INITIAL_LENGTH);
|
| 100 | + |
| 101 | + // Try the largest allowed power-of-two entry store size, which is 2^31 bytes |
| 102 | + // (Note that the 2^23 *length* gets converted to a 2^24 *capacity*.) |
| 103 | + PLDHashTable t2(PL_DHashGetStubOps(), (uint32_t)1 << 23, (uint32_t)1 << 7); |
| 104 | + |
| 105 | + // Try a too-large capacity (which aborts). |
| 106 | + TestCrashyOperation(InitCapacityOk_InitialLengthTooBig); |
| 107 | + |
| 108 | + // Try a large capacity combined with a large entry size that when multiplied |
| 109 | + // overflow (causing abort). |
| 110 | + TestCrashyOperation(InitCapacityOk_InitialEntryStoreTooBig); |
| 111 | + |
| 112 | + // Ideally we'd also try a large-but-ok capacity that almost but doesn't |
| 113 | + // quite overflow, but that would result in allocating slightly less than 4 |
| 114 | + // GiB of entry storage. That would be very likely to fail on 32-bit |
| 115 | + // platforms, so such a test wouldn't be reliable. |
30 | 116 | }
|
31 | 117 |
|
32 | 118 | TEST(PLDHashTableTest, LazyStorage)
|
@@ -141,7 +227,7 @@ TEST(PLDHashTableTest, Clear)
|
141 | 227 | ASSERT_EQ(t1.EntryCount(), 0u);
|
142 | 228 | }
|
143 | 229 |
|
144 |
| -TEST(PLDHashTableIterator, Iterator) |
| 230 | +TEST(PLDHashTableTest, Iterator) |
145 | 231 | {
|
146 | 232 | PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
|
147 | 233 |
|
|
0 commit comments