Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / tcmalloc / chromium / src / tests / tcmalloc_unittest.cc
blobcfdc79c1f2345f5035f0d1d8cf11d8d43c803ee2
1 // Copyright (c) 2005, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 // ---
31 // Unittest for the TCMalloc implementation.
33 // * The test consists of a set of threads.
34 // * Each thread maintains a set of allocated objects, with
35 // a bound on the total amount of data in the set.
36 // * Each allocated object's contents are generated by
37 // hashing the object pointer, and a generation count
38 // in the object. This allows us to easily check for
39 // data corruption.
40 // * At any given step, the thread can do any of the following:
41 // a. Allocate an object
42 // b. Increment an object's generation count and update
43 // its contents.
44 // c. Pass the object to another thread
45 // d. Free an object
46 // Also, at the end of every step, object(s) are freed to maintain
47 // the memory upper-bound.
49 // If this test is compiled with -DDEBUGALLOCATION, then we don't
50 // run some tests that test the inner workings of tcmalloc and
51 // break on debugallocation: that certain allocations are aligned
52 // in a certain way (even though no standard requires it), and that
53 // realloc() tries to minimize copying (which debug allocators don't
54 // care about).
56 #include "config_for_unittests.h"
57 // Complicated ordering requirements. tcmalloc.h defines (indirectly)
58 // _POSIX_C_SOURCE, which it needs so stdlib.h defines posix_memalign.
59 // unistd.h, on the other hand, requires _POSIX_C_SOURCE to be unset,
60 // at least on FreeBSD, in order to define sbrk. The solution
61 // is to #include unistd.h first. This is safe because unistd.h
62 // doesn't sub-include stdlib.h, so we'll still get posix_memalign
63 // when we #include stdlib.h. Blah.
64 #ifdef HAVE_UNISTD_H
65 #include <unistd.h> // for testing sbrk hooks
66 #endif
67 #include "tcmalloc.h" // must come early, to pick up posix_memalign
68 #include <stdlib.h>
69 #include <string.h>
70 #include <stdio.h>
71 #if defined HAVE_STDINT_H
72 #include <stdint.h> // for intptr_t
73 #endif
74 #include <sys/types.h> // for size_t
75 #ifdef HAVE_FCNTL_H
76 #include <fcntl.h> // for open; used with mmap-hook test
77 #endif
78 #ifdef HAVE_MMAP
79 #include <sys/mman.h> // for testing mmap hooks
80 #endif
81 #ifdef HAVE_MALLOC_H
82 #include <malloc.h> // defines pvalloc/etc on cygwin
83 #endif
84 #include <assert.h>
85 #include <vector>
86 #include <algorithm>
87 #include <string>
88 #include <new>
89 #include "base/logging.h"
90 #include "base/simple_mutex.h"
91 #include "gperftools/malloc_hook.h"
92 #include "gperftools/malloc_extension.h"
93 #include "gperftools/tcmalloc.h"
94 #include "thread_cache.h"
95 #include "tests/testutil.h"
97 // Windows doesn't define pvalloc and a few other obsolete unix
98 // functions; nor does it define posix_memalign (which is not obsolete).
99 #if defined(_WIN32)
100 # define cfree free // don't bother to try to test these obsolete fns
101 # define valloc malloc
102 # define pvalloc malloc
103 // I'd like to map posix_memalign to _aligned_malloc, but _aligned_malloc
104 // must be paired with _aligned_free (not normal free), which is too
105 // invasive a change to how we allocate memory here. So just bail
106 static bool kOSSupportsMemalign = false;
107 static inline void* Memalign(size_t align, size_t size) {
108 //LOG(FATAL) << "memalign not supported on windows";
109 exit(1);
110 return NULL;
112 static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
113 //LOG(FATAL) << "posix_memalign not supported on windows";
114 exit(1);
115 return -1;
118 // OS X defines posix_memalign in some OS versions but not others;
119 // it's confusing enough to check that it's easiest to just not to test.
120 #elif defined(__APPLE__)
121 static bool kOSSupportsMemalign = false;
122 static inline void* Memalign(size_t align, size_t size) {
123 //LOG(FATAL) << "memalign not supported on OS X";
124 exit(1);
125 return NULL;
127 static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
128 //LOG(FATAL) << "posix_memalign not supported on OS X";
129 exit(1);
130 return -1;
133 #else
134 static bool kOSSupportsMemalign = true;
135 static inline void* Memalign(size_t align, size_t size) {
136 return memalign(align, size);
138 static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
139 return posix_memalign(ptr, align, size);
142 #endif
144 // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old
145 // form of the name instead.
146 #ifndef MAP_ANONYMOUS
147 # define MAP_ANONYMOUS MAP_ANON
148 #endif
150 #define LOGSTREAM stdout
152 using std::vector;
153 using std::string;
155 DECLARE_double(tcmalloc_release_rate);
156 DECLARE_int32(max_free_queue_size); // in debugallocation.cc
157 DECLARE_int64(tcmalloc_sample_parameter);
159 namespace testing {
161 static const int FLAGS_numtests = 50000;
162 static const int FLAGS_log_every_n_tests = 50000; // log exactly once
164 // Testing parameters
165 static const int FLAGS_lgmaxsize = 16; // lg() of the max size object to alloc
166 static const int FLAGS_numthreads = 10; // Number of threads
167 static const int FLAGS_threadmb = 4; // Max memory size allocated by thread
168 static const int FLAGS_lg_max_memalign = 18; // lg of max alignment for memalign
170 static const double FLAGS_memalign_min_fraction = 0; // min expected%
171 static const double FLAGS_memalign_max_fraction = 0.4; // max expected%
172 static const double FLAGS_memalign_max_alignment_ratio = 6; // alignment/size
174 // Weights of different operations
175 static const int FLAGS_allocweight = 50; // Weight for picking allocation
176 static const int FLAGS_freeweight = 50; // Weight for picking free
177 static const int FLAGS_updateweight = 10; // Weight for picking update
178 static const int FLAGS_passweight = 1; // Weight for passing object
180 static const int kSizeBits = 8 * sizeof(size_t);
181 static const size_t kMaxSize = ~static_cast<size_t>(0);
182 static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1);
184 static const size_t kNotTooBig = 100000;
185 // We want an allocation that is definitely more than main memory. OS
186 // X has special logic to discard very big allocs before even passing
187 // the request along to the user-defined memory allocator; we're not
188 // interested in testing their logic, so we have to make sure we're
189 // not *too* big.
190 static const size_t kTooBig = kMaxSize - 100000;
192 static int news_handled = 0;
194 // Global array of threads
195 class TesterThread;
196 static TesterThread** threads;
198 // To help with generating random numbers
199 class TestHarness {
200 private:
201 // Information kept per type
202 struct Type {
203 string name;
204 int type;
205 int weight;
208 public:
209 TestHarness(int seed)
210 : types_(new vector<Type>), total_weight_(0), num_tests_(0) {
211 srandom(seed);
213 ~TestHarness() {
214 delete types_;
217 // Add operation type with specified weight. When starting a new
218 // iteration, an operation type is picked with probability
219 // proportional to its weight.
221 // "type" must be non-negative.
222 // "weight" must be non-negative.
223 void AddType(int type, int weight, const char* name);
225 // Call this to get the type of operation for the next iteration.
226 // It returns a random operation type from the set of registered
227 // operations. Returns -1 if tests should finish.
228 int PickType();
230 // If n == 0, returns the next pseudo-random number in the range [0 .. 0]
231 // If n != 0, returns the next pseudo-random number in the range [0 .. n)
232 int Uniform(int n) {
233 if (n == 0) {
234 return random() * 0;
235 } else {
236 return random() % n;
239 // Pick "base" uniformly from range [0,max_log] and then return
240 // "base" random bits. The effect is to pick a number in the range
241 // [0,2^max_log-1] with bias towards smaller numbers.
242 int Skewed(int max_log) {
243 const int base = random() % (max_log+1);
244 return random() % (1 << base);
247 private:
248 vector<Type>* types_; // Registered types
249 int total_weight_; // Total weight of all types
250 int num_tests_; // Num tests run so far
253 void TestHarness::AddType(int type, int weight, const char* name) {
254 Type t;
255 t.name = name;
256 t.type = type;
257 t.weight = weight;
258 types_->push_back(t);
259 total_weight_ += weight;
262 int TestHarness::PickType() {
263 if (num_tests_ >= FLAGS_numtests) return -1;
264 num_tests_++;
266 assert(total_weight_ > 0);
267 // This is a little skewed if total_weight_ doesn't divide 2^31, but it's close
268 int v = Uniform(total_weight_);
269 int i;
270 for (i = 0; i < types_->size(); i++) {
271 v -= (*types_)[i].weight;
272 if (v < 0) {
273 break;
277 assert(i < types_->size());
278 if ((num_tests_ % FLAGS_log_every_n_tests) == 0) {
279 fprintf(LOGSTREAM, " Test %d out of %d: %s\n",
280 num_tests_, FLAGS_numtests, (*types_)[i].name.c_str());
282 return (*types_)[i].type;
285 class AllocatorState : public TestHarness {
286 public:
287 explicit AllocatorState(int seed) : TestHarness(seed), memalign_fraction_(0) {
288 if (kOSSupportsMemalign) {
289 CHECK_GE(FLAGS_memalign_max_fraction, 0);
290 CHECK_LE(FLAGS_memalign_max_fraction, 1);
291 CHECK_GE(FLAGS_memalign_min_fraction, 0);
292 CHECK_LE(FLAGS_memalign_min_fraction, 1);
293 double delta = FLAGS_memalign_max_fraction - FLAGS_memalign_min_fraction;
294 CHECK_GE(delta, 0);
295 memalign_fraction_ = (Uniform(10000)/10000.0 * delta +
296 FLAGS_memalign_min_fraction);
297 //fprintf(LOGSTREAM, "memalign fraction: %f\n", memalign_fraction_);
300 virtual ~AllocatorState() {}
302 // Allocate memory. Randomly choose between malloc() or posix_memalign().
303 void* alloc(size_t size) {
304 if (Uniform(100) < memalign_fraction_ * 100) {
305 // Try a few times to find a reasonable alignment, or fall back on malloc.
306 for (int i = 0; i < 5; i++) {
307 size_t alignment = 1 << Uniform(FLAGS_lg_max_memalign);
308 if (alignment >= sizeof(intptr_t) &&
309 (size < sizeof(intptr_t) ||
310 alignment < FLAGS_memalign_max_alignment_ratio * size)) {
311 void *result = reinterpret_cast<void*>(static_cast<intptr_t>(0x1234));
312 int err = PosixMemalign(&result, alignment, size);
313 if (err != 0) {
314 CHECK_EQ(err, ENOMEM);
316 return err == 0 ? result : NULL;
320 return malloc(size);
323 private:
324 double memalign_fraction_;
328 // Info kept per thread
329 class TesterThread {
330 private:
331 // Info kept per allocated object
332 struct Object {
333 char* ptr; // Allocated pointer
334 int size; // Allocated size
335 int generation; // Generation counter of object contents
338 Mutex lock_; // For passing in another thread's obj
339 int id_; // My thread id
340 AllocatorState rnd_; // For generating random numbers
341 vector<Object> heap_; // This thread's heap
342 vector<Object> passed_; // Pending objects passed from others
343 size_t heap_size_; // Current heap size
344 int locks_ok_; // Number of OK TryLock() ops
345 int locks_failed_; // Number of failed TryLock() ops
347 // Type of operations
348 enum Type { ALLOC, FREE, UPDATE, PASS };
350 // ACM minimal standard random number generator. (re-entrant.)
351 class ACMRandom {
352 int32 seed_;
353 public:
354 explicit ACMRandom(int32 seed) { seed_ = seed; }
355 int32 Next() {
356 const int32 M = 2147483647L; // 2^31-1
357 const int32 A = 16807;
358 // In effect, we are computing seed_ = (seed_ * A) % M, where M = 2^31-1
359 uint32 lo = A * (int32)(seed_ & 0xFFFF);
360 uint32 hi = A * (int32)((uint32)seed_ >> 16);
361 lo += (hi & 0x7FFF) << 16;
362 if (lo > M) {
363 lo &= M;
364 ++lo;
366 lo += hi >> 15;
367 if (lo > M) {
368 lo &= M;
369 ++lo;
371 return (seed_ = (int32) lo);
375 public:
376 TesterThread(int id)
377 : id_(id),
378 rnd_(id+1),
379 heap_size_(0),
380 locks_ok_(0),
381 locks_failed_(0) {
384 virtual ~TesterThread() {
385 if (FLAGS_verbose)
386 fprintf(LOGSTREAM, "Thread %2d: locks %6d ok; %6d trylocks failed\n",
387 id_, locks_ok_, locks_failed_);
388 if (locks_ok_ + locks_failed_ >= 1000) {
389 CHECK_LE(locks_failed_, locks_ok_ / 2);
393 virtual void Run() {
394 rnd_.AddType(ALLOC, FLAGS_allocweight, "allocate");
395 rnd_.AddType(FREE, FLAGS_freeweight, "free");
396 rnd_.AddType(UPDATE, FLAGS_updateweight, "update");
397 rnd_.AddType(PASS, FLAGS_passweight, "pass");
399 while (true) {
400 AcquirePassedObjects();
402 switch (rnd_.PickType()) {
403 case ALLOC: AllocateObject(); break;
404 case FREE: FreeObject(); break;
405 case UPDATE: UpdateObject(); break;
406 case PASS: PassObject(); break;
407 case -1: goto done;
408 default: assert(NULL == "Unknown type");
411 ShrinkHeap();
414 done:
415 DeleteHeap();
418 // Allocate a new object
419 void AllocateObject() {
420 Object object;
421 object.size = rnd_.Skewed(FLAGS_lgmaxsize);
422 object.ptr = static_cast<char*>(rnd_.alloc(object.size));
423 CHECK(object.ptr);
424 object.generation = 0;
425 FillContents(&object);
426 heap_.push_back(object);
427 heap_size_ += object.size;
430 // Mutate a random object
431 void UpdateObject() {
432 if (heap_.empty()) return;
433 const int index = rnd_.Uniform(heap_.size());
434 CheckContents(heap_[index]);
435 heap_[index].generation++;
436 FillContents(&heap_[index]);
439 // Free a random object
440 void FreeObject() {
441 if (heap_.empty()) return;
442 const int index = rnd_.Uniform(heap_.size());
443 Object object = heap_[index];
444 CheckContents(object);
445 free(object.ptr);
446 heap_size_ -= object.size;
447 heap_[index] = heap_[heap_.size()-1];
448 heap_.pop_back();
451 // Delete all objects in the heap
452 void DeleteHeap() {
453 while (!heap_.empty()) {
454 FreeObject();
458 // Free objects until our heap is small enough
459 void ShrinkHeap() {
460 while (heap_size_ > FLAGS_threadmb << 20) {
461 assert(!heap_.empty());
462 FreeObject();
466 // Pass a random object to another thread
467 void PassObject() {
468 // Pick object to pass
469 if (heap_.empty()) return;
470 const int index = rnd_.Uniform(heap_.size());
471 Object object = heap_[index];
472 CheckContents(object);
474 // Pick thread to pass
475 const int tid = rnd_.Uniform(FLAGS_numthreads);
476 TesterThread* thread = threads[tid];
478 if (thread->lock_.TryLock()) {
479 // Pass the object
480 locks_ok_++;
481 thread->passed_.push_back(object);
482 thread->lock_.Unlock();
483 heap_size_ -= object.size;
484 heap_[index] = heap_[heap_.size()-1];
485 heap_.pop_back();
486 } else {
487 locks_failed_++;
491 // Grab any objects passed to this thread by another thread
492 void AcquirePassedObjects() {
493 // We do not create unnecessary contention by always using
494 // TryLock(). Plus we unlock immediately after swapping passed
495 // objects into a local vector.
496 vector<Object> copy;
497 { // Locking scope
498 if (!lock_.TryLock()) {
499 locks_failed_++;
500 return;
502 locks_ok_++;
503 swap(copy, passed_);
504 lock_.Unlock();
507 for (int i = 0; i < copy.size(); ++i) {
508 const Object& object = copy[i];
509 CheckContents(object);
510 heap_.push_back(object);
511 heap_size_ += object.size;
515 // Fill object contents according to ptr/generation
516 void FillContents(Object* object) {
517 ACMRandom r(reinterpret_cast<intptr_t>(object->ptr) & 0x7fffffff);
518 for (int i = 0; i < object->generation; ++i) {
519 r.Next();
521 const char c = static_cast<char>(r.Next());
522 memset(object->ptr, c, object->size);
525 // Check object contents
526 void CheckContents(const Object& object) {
527 ACMRandom r(reinterpret_cast<intptr_t>(object.ptr) & 0x7fffffff);
528 for (int i = 0; i < object.generation; ++i) {
529 r.Next();
532 // For large objects, we just check a prefix/suffix
533 const char expected = static_cast<char>(r.Next());
534 const int limit1 = object.size < 32 ? object.size : 32;
535 const int start2 = limit1 > object.size - 32 ? limit1 : object.size - 32;
536 for (int i = 0; i < limit1; ++i) {
537 CHECK_EQ(object.ptr[i], expected);
539 for (int i = start2; i < object.size; ++i) {
540 CHECK_EQ(object.ptr[i], expected);
545 static void RunThread(int thread_id) {
546 threads[thread_id]->Run();
549 static void TryHugeAllocation(size_t s, AllocatorState* rnd) {
550 void* p = rnd->alloc(s);
551 CHECK(p == NULL); // huge allocation s should fail!
554 static void TestHugeAllocations(AllocatorState* rnd) {
555 // Check that asking for stuff tiny bit smaller than largest possible
556 // size returns NULL.
557 for (size_t i = 0; i < 70000; i += rnd->Uniform(20)) {
558 TryHugeAllocation(kMaxSize - i, rnd);
560 // Asking for memory sizes near signed/unsigned boundary (kMaxSignedSize)
561 // might work or not, depending on the amount of virtual memory.
562 #ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs
563 for (size_t i = 0; i < 100; i++) {
564 void* p = NULL;
565 p = rnd->alloc(kMaxSignedSize + i);
566 if (p) free(p); // if: free(NULL) is not necessarily defined
567 p = rnd->alloc(kMaxSignedSize - i);
568 if (p) free(p);
570 #endif
572 // Check that ReleaseFreeMemory has no visible effect (aka, does not
573 // crash the test):
574 MallocExtension* inst = MallocExtension::instance();
575 CHECK(inst);
576 inst->ReleaseFreeMemory();
579 static void TestCalloc(size_t n, size_t s, bool ok) {
580 char* p = reinterpret_cast<char*>(calloc(n, s));
581 if (FLAGS_verbose)
582 fprintf(LOGSTREAM, "calloc(%"PRIxS", %"PRIxS"): %p\n", n, s, p);
583 if (!ok) {
584 CHECK(p == NULL); // calloc(n, s) should not succeed
585 } else {
586 CHECK(p != NULL); // calloc(n, s) should succeed
587 for (int i = 0; i < n*s; i++) {
588 CHECK(p[i] == '\0');
590 free(p);
594 // This makes sure that reallocing a small number of bytes in either
595 // direction doesn't cause us to allocate new memory.
596 static void TestRealloc() {
597 #ifndef DEBUGALLOCATION // debug alloc doesn't try to minimize reallocs
598 // When sampling, we always allocate in units of page-size, which
599 // makes reallocs of small sizes do extra work (thus, failing these
600 // checks). Since sampling is random, we turn off sampling to make
601 // sure that doesn't happen to us here.
602 const int64 old_sample_parameter = FLAGS_tcmalloc_sample_parameter;
603 FLAGS_tcmalloc_sample_parameter = 0; // turn off sampling
605 int start_sizes[] = { 100, 1000, 10000, 100000 };
606 int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 };
608 for (int s = 0; s < sizeof(start_sizes)/sizeof(*start_sizes); ++s) {
609 void* p = malloc(start_sizes[s]);
610 CHECK(p);
611 // The larger the start-size, the larger the non-reallocing delta.
612 for (int d = 0; d < (s+1) * 2; ++d) {
613 void* new_p = realloc(p, start_sizes[s] + deltas[d]);
614 CHECK(p == new_p); // realloc should not allocate new memory
616 // Test again, but this time reallocing smaller first.
617 for (int d = 0; d < s*2; ++d) {
618 void* new_p = realloc(p, start_sizes[s] - deltas[d]);
619 CHECK(p == new_p); // realloc should not allocate new memory
621 free(p);
623 FLAGS_tcmalloc_sample_parameter = old_sample_parameter;
624 #endif
627 static void TestNewHandler() throw (std::bad_alloc) {
628 ++news_handled;
629 throw std::bad_alloc();
632 static void TestOneNew(void* (*func)(size_t)) {
633 // success test
634 try {
635 void* ptr = (*func)(kNotTooBig);
636 if (0 == ptr) {
637 fprintf(LOGSTREAM, "allocation should not have failed.\n");
638 abort();
640 } catch (...) {
641 fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
642 abort();
645 // failure test
646 // we should always receive a bad_alloc exception
647 try {
648 (*func)(kTooBig);
649 fprintf(LOGSTREAM, "allocation should have failed.\n");
650 abort();
651 } catch (const std::bad_alloc&) {
652 // correct
653 } catch (...) {
654 fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
655 abort();
659 static void TestNew(void* (*func)(size_t)) {
660 news_handled = 0;
662 // test without new_handler:
663 std::new_handler saved_handler = std::set_new_handler(0);
664 TestOneNew(func);
666 // test with new_handler:
667 std::set_new_handler(TestNewHandler);
668 TestOneNew(func);
669 if (news_handled != 1) {
670 fprintf(LOGSTREAM, "new_handler was not called.\n");
671 abort();
673 std::set_new_handler(saved_handler);
676 static void TestOneNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) {
677 // success test
678 try {
679 void* ptr = (*func)(kNotTooBig, std::nothrow);
680 if (0 == ptr) {
681 fprintf(LOGSTREAM, "allocation should not have failed.\n");
682 abort();
684 } catch (...) {
685 fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
686 abort();
689 // failure test
690 // we should always receive a bad_alloc exception
691 try {
692 if ((*func)(kTooBig, std::nothrow) != 0) {
693 fprintf(LOGSTREAM, "allocation should have failed.\n");
694 abort();
696 } catch (...) {
697 fprintf(LOGSTREAM, "nothrow allocation threw unexpected exception.\n");
698 abort();
702 static void TestNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) {
703 news_handled = 0;
705 // test without new_handler:
706 std::new_handler saved_handler = std::set_new_handler(0);
707 TestOneNothrowNew(func);
709 // test with new_handler:
710 std::set_new_handler(TestNewHandler);
711 TestOneNothrowNew(func);
712 if (news_handled != 1) {
713 fprintf(LOGSTREAM, "nothrow new_handler was not called.\n");
714 abort();
716 std::set_new_handler(saved_handler);
720 // These are used as callbacks by the sanity-check. Set* and Reset*
721 // register the hook that counts how many times the associated memory
722 // function is called. After each such call, call Verify* to verify
723 // that we used the tcmalloc version of the call, and not the libc.
724 // Note the ... in the hook signature: we don't care what arguments
725 // the hook takes.
726 #define MAKE_HOOK_CALLBACK(hook_type) \
727 static int g_##hook_type##_calls = 0; \
728 static void IncrementCallsTo##hook_type(...) { \
729 g_##hook_type##_calls++; \
731 static void Verify##hook_type##WasCalled() { \
732 CHECK_GT(g_##hook_type##_calls, 0); \
733 g_##hook_type##_calls = 0; /* reset for next call */ \
735 static void Set##hook_type() { \
736 CHECK(MallocHook::Add##hook_type( \
737 (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
739 static void Reset##hook_type() { \
740 CHECK(MallocHook::Remove##hook_type( \
741 (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
744 // We do one for each hook typedef in malloc_hook.h
745 MAKE_HOOK_CALLBACK(NewHook);
746 MAKE_HOOK_CALLBACK(DeleteHook);
747 MAKE_HOOK_CALLBACK(MmapHook);
748 MAKE_HOOK_CALLBACK(MremapHook);
749 MAKE_HOOK_CALLBACK(MunmapHook);
750 MAKE_HOOK_CALLBACK(SbrkHook);
752 static void TestAlignmentForSize(int size) {
753 fprintf(LOGSTREAM, "Testing alignment of malloc(%d)\n", size);
754 static const int kNum = 100;
755 void* ptrs[kNum];
756 for (int i = 0; i < kNum; i++) {
757 ptrs[i] = malloc(size);
758 uintptr_t p = reinterpret_cast<uintptr_t>(ptrs[i]);
759 CHECK((p % sizeof(void*)) == 0);
760 CHECK((p % sizeof(double)) == 0);
762 // Must have 16-byte alignment for large enough objects
763 if (size >= 16) {
764 CHECK((p % 16) == 0);
767 for (int i = 0; i < kNum; i++) {
768 free(ptrs[i]);
772 static void TestMallocAlignment() {
773 for (int lg = 0; lg < 16; lg++) {
774 TestAlignmentForSize((1<<lg) - 1);
775 TestAlignmentForSize((1<<lg) + 0);
776 TestAlignmentForSize((1<<lg) + 1);
780 static void TestHugeThreadCache() {
781 fprintf(LOGSTREAM, "==== Testing huge thread cache\n");
782 // More than 2^16 to cause integer overflow of 16 bit counters.
783 static const int kNum = 70000;
784 char** array = new char*[kNum];
785 for (int i = 0; i < kNum; ++i) {
786 array[i] = new char[10];
788 for (int i = 0; i < kNum; ++i) {
789 delete[] array[i];
791 delete[] array;
794 namespace {
796 struct RangeCallbackState {
797 uintptr_t ptr;
798 base::MallocRange::Type expected_type;
799 size_t min_size;
800 bool matched;
803 static void RangeCallback(void* arg, const base::MallocRange* r) {
804 RangeCallbackState* state = reinterpret_cast<RangeCallbackState*>(arg);
805 if (state->ptr >= r->address &&
806 state->ptr < r->address + r->length) {
807 if (state->expected_type == base::MallocRange::FREE) {
808 // We are expecting r->type == FREE, but ReleaseMemory
809 // may have already moved us to UNMAPPED state instead (this happens in
810 // approximately 0.1% of executions). Accept either state.
811 CHECK(r->type == base::MallocRange::FREE ||
812 r->type == base::MallocRange::UNMAPPED);
813 } else {
814 CHECK_EQ(r->type, state->expected_type);
816 CHECK_GE(r->length, state->min_size);
817 state->matched = true;
821 // Check that at least one of the callbacks from Ranges() contains
822 // the specified address with the specified type, and has size
823 // >= min_size.
824 static void CheckRangeCallback(void* ptr, base::MallocRange::Type type,
825 size_t min_size) {
826 RangeCallbackState state;
827 state.ptr = reinterpret_cast<uintptr_t>(ptr);
828 state.expected_type = type;
829 state.min_size = min_size;
830 state.matched = false;
831 MallocExtension::instance()->Ranges(&state, RangeCallback);
832 CHECK(state.matched);
837 static void TestRanges() {
838 static const int MB = 1048576;
839 void* a = malloc(MB);
840 void* b = malloc(MB);
841 CheckRangeCallback(a, base::MallocRange::INUSE, MB);
842 CheckRangeCallback(b, base::MallocRange::INUSE, MB);
843 free(a);
844 CheckRangeCallback(a, base::MallocRange::FREE, MB);
845 CheckRangeCallback(b, base::MallocRange::INUSE, MB);
846 MallocExtension::instance()->ReleaseFreeMemory();
847 CheckRangeCallback(a, base::MallocRange::UNMAPPED, MB);
848 CheckRangeCallback(b, base::MallocRange::INUSE, MB);
849 free(b);
850 CheckRangeCallback(a, base::MallocRange::UNMAPPED, MB);
851 CheckRangeCallback(b, base::MallocRange::FREE, MB);
854 #ifndef DEBUGALLOCATION
855 static size_t GetUnmappedBytes() {
856 size_t bytes;
857 CHECK(MallocExtension::instance()->GetNumericProperty(
858 "tcmalloc.pageheap_unmapped_bytes", &bytes));
859 return bytes;
861 #endif
863 static void TestReleaseToSystem() {
864 // Debug allocation mode adds overhead to each allocation which
865 // messes up all the equality tests here. I just disable the
866 // teset in this mode. TODO(csilvers): get it to work for debugalloc?
867 #ifndef DEBUGALLOCATION
868 const double old_tcmalloc_release_rate = FLAGS_tcmalloc_release_rate;
869 FLAGS_tcmalloc_release_rate = 0;
871 static const int MB = 1048576;
872 void* a = malloc(MB);
873 void* b = malloc(MB);
874 MallocExtension::instance()->ReleaseFreeMemory();
875 size_t starting_bytes = GetUnmappedBytes();
877 // Calling ReleaseFreeMemory() a second time shouldn't do anything.
878 MallocExtension::instance()->ReleaseFreeMemory();
879 EXPECT_EQ(starting_bytes, GetUnmappedBytes());
881 // ReleaseToSystem shouldn't do anything either.
882 MallocExtension::instance()->ReleaseToSystem(MB);
883 EXPECT_EQ(starting_bytes, GetUnmappedBytes());
885 free(a);
887 // The span to release should be 1MB.
888 MallocExtension::instance()->ReleaseToSystem(MB/2);
889 EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
891 // Should do nothing since the previous call released too much.
892 MallocExtension::instance()->ReleaseToSystem(MB/4);
893 EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
895 free(b);
897 // Use up the extra MB/4 bytes from 'a' and also release 'b'.
898 MallocExtension::instance()->ReleaseToSystem(MB/2);
899 EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
901 // Should do nothing since the previous call released too much.
902 MallocExtension::instance()->ReleaseToSystem(MB/2);
903 EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
905 // Nothing else to release.
906 MallocExtension::instance()->ReleaseFreeMemory();
907 EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
909 a = malloc(MB);
910 free(a);
911 EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
913 // Releasing less than a page should still trigger a release.
914 MallocExtension::instance()->ReleaseToSystem(1);
915 EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
917 FLAGS_tcmalloc_release_rate = old_tcmalloc_release_rate;
918 #endif // #ifndef DEBUGALLOCATION
921 // On MSVC10, in release mode, the optimizer convinces itself
922 // g_no_memory is never changed (I guess it doesn't realize OnNoMemory
923 // might be called). Work around this by setting the var volatile.
924 volatile bool g_no_memory = false;
925 std::new_handler g_old_handler = NULL;
926 static void OnNoMemory() {
927 g_no_memory = true;
928 std::set_new_handler(g_old_handler);
931 static void TestSetNewMode() {
932 int old_mode = tc_set_new_mode(1);
934 g_old_handler = std::set_new_handler(&OnNoMemory);
935 g_no_memory = false;
936 void* ret = malloc(kTooBig);
937 EXPECT_EQ(NULL, ret);
938 EXPECT_TRUE(g_no_memory);
940 g_old_handler = std::set_new_handler(&OnNoMemory);
941 g_no_memory = false;
942 ret = calloc(1, kTooBig);
943 EXPECT_EQ(NULL, ret);
944 EXPECT_TRUE(g_no_memory);
946 g_old_handler = std::set_new_handler(&OnNoMemory);
947 g_no_memory = false;
948 ret = realloc(NULL, kTooBig);
949 EXPECT_EQ(NULL, ret);
950 EXPECT_TRUE(g_no_memory);
952 if (kOSSupportsMemalign) {
953 // Not really important, but must be small enough such that
954 // kAlignment + kTooBig does not overflow.
955 const int kAlignment = 1 << 5;
957 g_old_handler = std::set_new_handler(&OnNoMemory);
958 g_no_memory = false;
959 ret = Memalign(kAlignment, kTooBig);
960 EXPECT_EQ(NULL, ret);
961 EXPECT_TRUE(g_no_memory);
963 g_old_handler = std::set_new_handler(&OnNoMemory);
964 g_no_memory = false;
965 EXPECT_EQ(ENOMEM,
966 PosixMemalign(&ret, kAlignment, kTooBig));
967 EXPECT_EQ(NULL, ret);
968 EXPECT_TRUE(g_no_memory);
971 tc_set_new_mode(old_mode);
974 static int RunAllTests(int argc, char** argv) {
975 // Optional argv[1] is the seed
976 AllocatorState rnd(argc > 1 ? atoi(argv[1]) : 100);
978 SetTestResourceLimit();
980 // TODO(odo): This test has been disabled because it is only by luck that it
981 // does not result in fragmentation. When tcmalloc makes an allocation which
982 // spans previously unused leaves of the pagemap it will allocate and fill in
983 // the leaves to cover the new allocation. The leaves happen to be 256MiB in
984 // the 64-bit build, and with the sbrk allocator these allocations just
985 // happen to fit in one leaf by luck. With other allocators (mmap,
986 // memfs_malloc when used with small pages) the allocations generally span
987 // two leaves and this results in a very bad fragmentation pattern with this
988 // code. The same failure can be forced with the sbrk allocator just by
989 // allocating something on the order of 128MiB prior to starting this test so
990 // that the test allocations straddle a 256MiB boundary.
992 // TODO(csilvers): port MemoryUsage() over so the test can use that
993 #if 0
994 # include <unistd.h> // for getpid()
995 // Allocate and deallocate blocks of increasing sizes to check if the alloc
996 // metadata fragments the memory. (Do not put other allocations/deallocations
997 // before this test, it may break).
999 size_t memory_usage = MemoryUsage(getpid());
1000 fprintf(LOGSTREAM, "Testing fragmentation\n");
1001 for ( int i = 200; i < 240; ++i ) {
1002 int size = i << 20;
1003 void *test1 = rnd.alloc(size);
1004 CHECK(test1);
1005 for ( int j = 0; j < size; j += (1 << 12) ) {
1006 static_cast<char*>(test1)[j] = 1;
1008 free(test1);
1010 // There may still be a bit of fragmentation at the beginning, until we
1011 // reach kPageMapBigAllocationThreshold bytes so we check for
1012 // 200 + 240 + margin.
1013 CHECK_LT(MemoryUsage(getpid()), memory_usage + (450 << 20) );
1015 #endif
1017 // Check that empty allocation works
1018 fprintf(LOGSTREAM, "Testing empty allocation\n");
1020 void* p1 = rnd.alloc(0);
1021 CHECK(p1 != NULL);
1022 void* p2 = rnd.alloc(0);
1023 CHECK(p2 != NULL);
1024 CHECK(p1 != p2);
1025 free(p1);
1026 free(p2);
1029 // This code stresses some of the memory allocation via STL.
1030 // It may call operator delete(void*, nothrow_t).
1031 fprintf(LOGSTREAM, "Testing STL use\n");
1033 std::vector<int> v;
1034 v.push_back(1);
1035 v.push_back(2);
1036 v.push_back(3);
1037 v.push_back(0);
1038 std::stable_sort(v.begin(), v.end());
1041 // Test each of the memory-allocation functions once, just as a sanity-check
1042 fprintf(LOGSTREAM, "Sanity-testing all the memory allocation functions\n");
1044 // We use new-hook and delete-hook to verify we actually called the
1045 // tcmalloc version of these routines, and not the libc version.
1046 SetNewHook(); // defined as part of MAKE_HOOK_CALLBACK, above
1047 SetDeleteHook(); // ditto
1049 void* p1 = malloc(10);
1050 CHECK(p1 != NULL); // force use of this variable
1051 VerifyNewHookWasCalled();
1052 // Also test the non-standard tc_malloc_size
1053 size_t actual_p1_size = tc_malloc_size(p1);
1054 CHECK_GE(actual_p1_size, 10);
1055 CHECK_LT(actual_p1_size, 100000); // a reasonable upper-bound, I think
1056 free(p1);
1057 VerifyDeleteHookWasCalled();
1060 p1 = calloc(10, 2);
1061 CHECK(p1 != NULL);
1062 VerifyNewHookWasCalled();
1063 // We make sure we realloc to a big size, since some systems (OS
1064 // X) will notice if the realloced size continues to fit into the
1065 // malloc-block and make this a noop if so.
1066 p1 = realloc(p1, 30000);
1067 CHECK(p1 != NULL);
1068 VerifyNewHookWasCalled();
1069 VerifyDeleteHookWasCalled();
1070 cfree(p1); // synonym for free
1071 VerifyDeleteHookWasCalled();
1073 if (kOSSupportsMemalign) {
1074 CHECK_EQ(PosixMemalign(&p1, sizeof(p1), 40), 0);
1075 CHECK(p1 != NULL);
1076 VerifyNewHookWasCalled();
1077 free(p1);
1078 VerifyDeleteHookWasCalled();
1080 p1 = Memalign(sizeof(p1) * 2, 50);
1081 CHECK(p1 != NULL);
1082 VerifyNewHookWasCalled();
1083 free(p1);
1084 VerifyDeleteHookWasCalled();
1087 // Windows has _aligned_malloc. Let's test that that's captured too.
1088 #if (defined(_MSC_VER) || defined(__MINGW32__)) && !defined(PERFTOOLS_NO_ALIGNED_MALLOC)
1089 p1 = _aligned_malloc(sizeof(p1) * 2, 64);
1090 CHECK(p1 != NULL);
1091 VerifyNewHookWasCalled();
1092 _aligned_free(p1);
1093 VerifyDeleteHookWasCalled();
1094 #endif
1096 p1 = valloc(60);
1097 CHECK(p1 != NULL);
1098 VerifyNewHookWasCalled();
1099 free(p1);
1100 VerifyDeleteHookWasCalled();
1102 p1 = pvalloc(70);
1103 CHECK(p1 != NULL);
1104 VerifyNewHookWasCalled();
1105 free(p1);
1106 VerifyDeleteHookWasCalled();
1108 char* p2 = new char;
1109 CHECK(p2 != NULL);
1110 VerifyNewHookWasCalled();
1111 delete p2;
1112 VerifyDeleteHookWasCalled();
1114 p2 = new char[100];
1115 CHECK(p2 != NULL);
1116 VerifyNewHookWasCalled();
1117 delete[] p2;
1118 VerifyDeleteHookWasCalled();
1120 p2 = new(std::nothrow) char;
1121 CHECK(p2 != NULL);
1122 VerifyNewHookWasCalled();
1123 delete p2;
1124 VerifyDeleteHookWasCalled();
1126 p2 = new(std::nothrow) char[100];
1127 CHECK(p2 != NULL);
1128 VerifyNewHookWasCalled();
1129 delete[] p2;
1130 VerifyDeleteHookWasCalled();
1132 // Another way of calling operator new
1133 p2 = static_cast<char*>(::operator new(100));
1134 CHECK(p2 != NULL);
1135 VerifyNewHookWasCalled();
1136 ::operator delete(p2);
1137 VerifyDeleteHookWasCalled();
1139 // Try to call nothrow's delete too. Compilers use this.
1140 p2 = static_cast<char*>(::operator new(100, std::nothrow));
1141 CHECK(p2 != NULL);
1142 VerifyNewHookWasCalled();
1143 ::operator delete(p2, std::nothrow);
1144 VerifyDeleteHookWasCalled();
1146 // Try strdup(), which the system allocates but we must free. If
1147 // all goes well, libc will use our malloc!
1148 p2 = strdup("test");
1149 CHECK(p2 != NULL);
1150 VerifyNewHookWasCalled();
1151 free(p2);
1152 VerifyDeleteHookWasCalled();
1155 // Test mmap too: both anonymous mmap and mmap of a file
1156 // Note that for right now we only override mmap on linux
1157 // systems, so those are the only ones for which we check.
1158 SetMmapHook();
1159 SetMremapHook();
1160 SetMunmapHook();
1161 #if defined(HAVE_MMAP) && defined(__linux) && \
1162 (defined(__i386__) || defined(__x86_64__))
1163 int size = 8192*2;
1164 p1 = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE,
1165 -1, 0);
1166 CHECK(p1 != NULL);
1167 VerifyMmapHookWasCalled();
1168 p1 = mremap(p1, size, size/2, 0);
1169 CHECK(p1 != NULL);
1170 VerifyMremapHookWasCalled();
1171 size /= 2;
1172 munmap(p1, size);
1173 VerifyMunmapHookWasCalled();
1175 int fd = open("/dev/zero", O_RDONLY);
1176 CHECK_GE(fd, 0); // make sure the open succeeded
1177 p1 = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
1178 CHECK(p1 != NULL);
1179 VerifyMmapHookWasCalled();
1180 munmap(p1, 8192);
1181 VerifyMunmapHookWasCalled();
1182 close(fd);
1183 #else // this is just to quiet the compiler: make sure all fns are called
1184 IncrementCallsToMmapHook();
1185 IncrementCallsToMunmapHook();
1186 IncrementCallsToMremapHook();
1187 VerifyMmapHookWasCalled();
1188 VerifyMremapHookWasCalled();
1189 VerifyMunmapHookWasCalled();
1190 #endif
1192 // Test sbrk
1193 SetSbrkHook();
1194 #if defined(HAVE_SBRK) && defined(__linux) && \
1195 (defined(__i386__) || defined(__x86_64__))
1196 p1 = sbrk(8192);
1197 CHECK(p1 != NULL);
1198 VerifySbrkHookWasCalled();
1199 p1 = sbrk(-8192);
1200 CHECK(p1 != NULL);
1201 VerifySbrkHookWasCalled();
1202 // However, sbrk hook should *not* be called with sbrk(0)
1203 p1 = sbrk(0);
1204 CHECK(p1 != NULL);
1205 CHECK_EQ(g_SbrkHook_calls, 0);
1206 #else // this is just to quiet the compiler: make sure all fns are called
1207 IncrementCallsToSbrkHook();
1208 VerifySbrkHookWasCalled();
1209 #endif
1211 // Reset the hooks to what they used to be. These are all
1212 // defined as part of MAKE_HOOK_CALLBACK, above.
1213 ResetNewHook();
1214 ResetDeleteHook();
1215 ResetMmapHook();
1216 ResetMremapHook();
1217 ResetMunmapHook();
1218 ResetSbrkHook();
1221 // Check that "lots" of memory can be allocated
1222 fprintf(LOGSTREAM, "Testing large allocation\n");
1224 const int mb_to_allocate = 100;
1225 void* p = rnd.alloc(mb_to_allocate << 20);
1226 CHECK(p != NULL); // could not allocate
1227 free(p);
1230 TestMallocAlignment();
1232 // Check calloc() with various arguments
1233 fprintf(LOGSTREAM, "Testing calloc\n");
1234 TestCalloc(0, 0, true);
1235 TestCalloc(0, 1, true);
1236 TestCalloc(1, 1, true);
1237 TestCalloc(1<<10, 0, true);
1238 TestCalloc(1<<20, 0, true);
1239 TestCalloc(0, 1<<10, true);
1240 TestCalloc(0, 1<<20, true);
1241 TestCalloc(1<<20, 2, true);
1242 TestCalloc(2, 1<<20, true);
1243 TestCalloc(1000, 1000, true);
1245 TestCalloc(kMaxSize, 2, false);
1246 TestCalloc(2, kMaxSize, false);
1247 TestCalloc(kMaxSize, kMaxSize, false);
1249 TestCalloc(kMaxSignedSize, 3, false);
1250 TestCalloc(3, kMaxSignedSize, false);
1251 TestCalloc(kMaxSignedSize, kMaxSignedSize, false);
1253 // Test that realloc doesn't always reallocate and copy memory.
1254 fprintf(LOGSTREAM, "Testing realloc\n");
1255 TestRealloc();
1257 fprintf(LOGSTREAM, "Testing operator new(nothrow).\n");
1258 TestNothrowNew(&::operator new);
1259 fprintf(LOGSTREAM, "Testing operator new[](nothrow).\n");
1260 TestNothrowNew(&::operator new[]);
1261 fprintf(LOGSTREAM, "Testing operator new.\n");
1262 TestNew(&::operator new);
1263 fprintf(LOGSTREAM, "Testing operator new[].\n");
1264 TestNew(&::operator new[]);
1266 // Create threads
1267 fprintf(LOGSTREAM, "Testing threaded allocation/deallocation (%d threads)\n",
1268 FLAGS_numthreads);
1269 threads = new TesterThread*[FLAGS_numthreads];
1270 for (int i = 0; i < FLAGS_numthreads; ++i) {
1271 threads[i] = new TesterThread(i);
1274 // This runs all the tests at the same time, with a 1M stack size each
1275 RunManyThreadsWithId(RunThread, FLAGS_numthreads, 1<<20);
1277 for (int i = 0; i < FLAGS_numthreads; ++i) delete threads[i]; // Cleanup
1279 // Do the memory intensive tests after threads are done, since exhausting
1280 // the available address space can make pthread_create to fail.
1282 // Check that huge allocations fail with NULL instead of crashing
1283 fprintf(LOGSTREAM, "Testing huge allocations\n");
1284 TestHugeAllocations(&rnd);
1286 // Check that large allocations fail with NULL instead of crashing
1287 #ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs
1288 fprintf(LOGSTREAM, "Testing out of memory\n");
1289 for (int s = 0; ; s += (10<<20)) {
1290 void* large_object = rnd.alloc(s);
1291 if (large_object == NULL) break;
1292 free(large_object);
1294 #endif
1296 TestHugeThreadCache();
1297 TestRanges();
1298 TestReleaseToSystem();
1299 TestSetNewMode();
1301 return 0;
1306 using testing::RunAllTests;
1308 int main(int argc, char** argv) {
1309 #ifdef DEBUGALLOCATION // debug allocation takes forever for huge allocs
1310 FLAGS_max_free_queue_size = 0; // return freed blocks to tcmalloc immediately
1311 #endif
1313 RunAllTests(argc, argv);
1315 // Test tc_version()
1316 fprintf(LOGSTREAM, "Testing tc_version()\n");
1317 int major;
1318 int minor;
1319 const char* patch;
1320 char mmp[64];
1321 const char* human_version = tc_version(&major, &minor, &patch);
1322 snprintf(mmp, sizeof(mmp), "%d.%d%s", major, minor, patch);
1323 CHECK(!strcmp(PACKAGE_STRING, human_version));
1324 CHECK(!strcmp(PACKAGE_VERSION, mmp));
1326 fprintf(LOGSTREAM, "PASS\n");