//===-- stacktest.cpp -----------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "klee/KDAlloc/kdalloc.h" #include "xoshiro.h" #if defined(USE_GTEST_INSTEAD_OF_MAIN) #include "gtest/gtest.h" #endif #include #include #include #include #include #include #include #include #include #include namespace { class RandomTest { xoshiro512 rng; #if defined(USE_KDALLOC) klee::kdalloc::StackAllocator allocator; #endif std::vector> allocations; std::geometric_distribution allocation_bin_distribution; std::geometric_distribution large_allocation_distribution; public: std::size_t maximum_concurrent_allocations = 0; std::uint64_t allocation_count = 0; std::uint64_t deallocation_count = 0; RandomTest(std::mt19937_64::result_type seed = 0x31337) : rng(seed) #if defined(USE_KDALLOC) , allocator(klee::kdalloc::StackAllocatorFactory( static_cast(1) << 42, 0)) #endif , allocation_bin_distribution(0.3), large_allocation_distribution(0.00003) { } void run(std::uint64_t const iterations) { allocations.reserve((iterations * 7) / 10); std::uniform_int_distribution choice(0, 999); for (std::uint64_t i = 0; i < iterations; ++i) { auto chosen = choice(rng); if (chosen < 650) { ++allocation_count; allocate_sized(); } else if (chosen < 700) { ++allocation_count; allocate_large(); } else if (chosen < 1000) { ++deallocation_count; deallocate(); } } cleanup(); } void cleanup() { while (!allocations.empty()) { #if defined(USE_KDALLOC) allocator.free(allocations.back().first, allocations.back().second); #else free(allocations.back().first); #endif allocations.pop_back(); } } void allocate_sized() { auto bin = allocation_bin_distribution(rng); while (bin >= 11) { bin = allocation_bin_distribution(rng); } auto min = (bin == 0 ? 1 : (static_cast(1) << (bin + 1)) + 1); auto max = static_cast(1) << (bin + 2); auto size = std::uniform_int_distribution(min, max)(rng); #if defined(USE_KDALLOC) allocations.emplace_back(allocator.allocate(size), size); #else allocations.emplace_back(std::malloc(size), size); #endif if (allocations.size() > maximum_concurrent_allocations) { maximum_concurrent_allocations = allocations.size(); } } void allocate_large() { auto size = 0; while (size <= 4096 || size > 1073741825) { size = large_allocation_distribution(rng) + 4097; } #if defined(USE_KDALLOC) allocations.emplace_back(allocator.allocate(size), size); #else allocations.emplace_back(std::malloc(size), size); #endif if (allocations.size() > maximum_concurrent_allocations) { maximum_concurrent_allocations = allocations.size(); } } void deallocate() { if (allocations.empty()) { return; } #if defined(USE_KDALLOC) allocator.free(allocations.back().first, allocations.back().second); #else free(allocations.back().first); #endif allocations.pop_back(); } }; } // namespace #if defined(USE_GTEST_INSTEAD_OF_MAIN) int stack_test() { #else int main() { #endif #if defined(USE_KDALLOC) std::cout << "Using kdalloc\n"; #else std::cout << "Using std::malloc\n"; #endif auto start = std::chrono::steady_clock::now(); RandomTest tester; tester.run(10'000'000); auto stop = std::chrono::steady_clock::now(); std::cout << std::dec << std::chrono::duration_cast(stop - start) .count() << " ms\n"; std::cout << "\n"; std::cout << "Allocations: " << tester.allocation_count << "\n"; std::cout << "Deallocations: " << tester.deallocation_count << "\n"; std::cout << "Maximum concurrent allocations: " << tester.maximum_concurrent_allocations << "\n"; exit(0); } #if defined(USE_GTEST_INSTEAD_OF_MAIN) TEST(KDAllocDeathTest, Stack) { ASSERT_EXIT(stack_test(), ::testing::ExitedWithCode(0), ""); } #endif