|
19 | 19 | #include <folly/Random.h>
|
20 | 20 | #include <folly/Singleton.h>
|
21 | 21 | #include <folly/synchronization/Baton.h>
|
| 22 | +#include <folly/synchronization/Latch.h> |
| 23 | + |
| 24 | +#include <fcntl.h> |
| 25 | +#include <unistd.h> |
| 26 | +#include <ctype.h> |
| 27 | +#include <semaphore.h> |
22 | 28 |
|
23 | 29 | #include <algorithm>
|
24 | 30 | #include <chrono>
|
@@ -5189,6 +5195,152 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> {
|
5189 | 5195 | }
|
5190 | 5196 | }
|
5191 | 5197 |
|
| 5198 | + // helper function to check if gdb is running and attached to test |
| 5199 | + bool isGdbAttached() { |
| 5200 | + char buf[4096]; |
| 5201 | + const int status_fd = open("/proc/self/status", O_RDONLY); |
| 5202 | + if (status_fd == -1) { |
| 5203 | + return false; |
| 5204 | + } |
| 5205 | + const ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1); |
| 5206 | + close(status_fd); |
| 5207 | + |
| 5208 | + if (num_read <= 0) { |
| 5209 | + return false; |
| 5210 | + } |
| 5211 | + buf[num_read] = '\0'; |
| 5212 | + constexpr char tracerPidString[] = "TracerPid:"; |
| 5213 | + const auto tracer_pid_ptr = strstr(buf, tracerPidString); |
| 5214 | + if (!tracer_pid_ptr) { |
| 5215 | + return false; |
| 5216 | + } |
| 5217 | + for (const char* characterPtr = tracer_pid_ptr + sizeof(tracerPidString) - 1; |
| 5218 | + characterPtr <= buf + num_read; ++characterPtr) { |
| 5219 | + if (isspace(*characterPtr)) { |
| 5220 | + continue; |
| 5221 | + } else { |
| 5222 | + return isdigit(*characterPtr) != 0 && *characterPtr != '0'; |
| 5223 | + } |
| 5224 | + } |
| 5225 | + return false; |
| 5226 | + } |
| 5227 | + |
| 5228 | + void gdb_sync1() { sleep(1); } |
| 5229 | + void gdb_sync2() { sleep(1); } |
| 5230 | + void gdb_sync3() { sleep(1); } |
| 5231 | + void gdb_sync4() { sleep(1); } |
| 5232 | + void testChainedItemParentAcquireAfterMove() { |
| 5233 | + sem_unlink("/gdb1_sem"); |
| 5234 | + sem_t *sem = sem_open("/gdb1_sem", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0); |
| 5235 | + int ppid = getpid(); //parent pid |
| 5236 | + int fpid = fork(); |
| 5237 | + if (fpid == 0) { |
| 5238 | + sem_wait(sem); |
| 5239 | + sem_close(sem); |
| 5240 | + sem_unlink("/gdb1_sem"); |
| 5241 | + char cmdpid[256]; |
| 5242 | + sprintf(cmdpid,"%d",ppid); |
| 5243 | + int f = execlp("gdb","gdb","--pid",cmdpid, |
| 5244 | + "--batch-silent","--command=ChainedItemParentAcquireAfterMove.gdb",(char*) 0); |
| 5245 | + ASSERT(f != -1); |
| 5246 | + } |
| 5247 | + |
| 5248 | + // create an allocator worth 4 slabs |
| 5249 | + // first slab is for overhead, second is parent class |
| 5250 | + // thrid is chained item 1 and fourth is for new chained item alloc |
| 5251 | + // to move to. |
| 5252 | + typename AllocatorT::Config config; |
| 5253 | + config.configureChainedItems(); |
| 5254 | + |
| 5255 | + config.setCacheSize(4 * Slab::kSize); |
| 5256 | + |
| 5257 | + using Item = typename AllocatorT::Item; |
| 5258 | + |
| 5259 | + std::atomic<uint64_t> numRemovedKeys{0}; |
| 5260 | + config.setRemoveCallback( |
| 5261 | + [&](const typename AllocatorT::RemoveCbData&) { ++numRemovedKeys; }); |
| 5262 | + |
| 5263 | + std::atomic<uint64_t> numMoves{0}; |
| 5264 | + config.enableMovingOnSlabRelease( |
| 5265 | + [&](Item& oldItem, Item& newItem, Item* /* parentPtr */) { |
| 5266 | + assert(oldItem.getSize() == newItem.getSize()); |
| 5267 | + assert(oldItem.isChainedItem()); |
| 5268 | + std::memcpy(newItem.getMemory(), oldItem.getMemory(), |
| 5269 | + oldItem.getSize()); |
| 5270 | + ++numMoves; |
| 5271 | + } |
| 5272 | + ); |
| 5273 | + |
| 5274 | + AllocatorT alloc(config); |
| 5275 | + const size_t numBytes = alloc.getCacheMemoryStats().ramCacheSize; |
| 5276 | + const auto poolSize = numBytes; |
| 5277 | + //items are 100K and 200K |
| 5278 | + const std::set<uint32_t> allocSizes = {2*1024*1024 + 1024 , 3*1024*1024}; |
| 5279 | + const auto pid = alloc.addPool("one", poolSize, allocSizes); |
| 5280 | + |
| 5281 | + // Allocate 1 parent items and for each parent item, 1 chained |
| 5282 | + // allocation |
| 5283 | + auto allocFn = [&](std::string keyPrefix, std::vector<uint32_t> sizes) { |
| 5284 | + for (unsigned int loop = 0; loop < 1; ++loop) { |
| 5285 | + std::vector<uint8_t*> bufList; |
| 5286 | + std::vector<typename AllocatorT::WriteHandle> parentHandles; |
| 5287 | + for (unsigned int i = 0; i < 1; ++i) { |
| 5288 | + const auto key = keyPrefix + folly::to<std::string>(loop) + "_" + |
| 5289 | + folly::to<std::string>(i); |
| 5290 | + |
| 5291 | + auto itemHandle = util::allocateAccessible(alloc, pid, key, sizes[0]); |
| 5292 | + |
| 5293 | + for (unsigned int j = 0; j < 1; ++j) { |
| 5294 | + auto childItem = |
| 5295 | + alloc.allocateChainedItem(itemHandle, sizes[1]); |
| 5296 | + ASSERT_NE(nullptr, childItem); |
| 5297 | + |
| 5298 | + uint8_t* buf = reinterpret_cast<uint8_t*>(childItem->getMemory()); |
| 5299 | + // write first 50 bytes here |
| 5300 | + for (uint8_t k = 0; k < 50; ++k) { |
| 5301 | + buf[k] = k; |
| 5302 | + } |
| 5303 | + bufList.push_back(buf); |
| 5304 | + |
| 5305 | + alloc.addChainedItem(itemHandle, std::move(childItem)); |
| 5306 | + } |
| 5307 | + } |
| 5308 | + } |
| 5309 | + }; |
| 5310 | + auto sizes = std::vector<uint32_t>{2*1024*1024, 3*1024*1024 - 1000}; |
| 5311 | + allocFn(std::string{"yolo"}, sizes); |
| 5312 | + |
| 5313 | + sem_post(sem); |
| 5314 | + while (!isGdbAttached()) { |
| 5315 | + std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 5316 | + } |
| 5317 | + //need to suspend thread 1 - who is doing the eviction |
| 5318 | + //gdb will do this for us |
| 5319 | + folly::Latch latch(1); |
| 5320 | + std::unique_ptr<std::thread> r = std::make_unique<std::thread>([&](){ |
| 5321 | + ClassId cid = static_cast<ClassId>(1); |
| 5322 | + //gdb will interupt this thread when we hit acquire |
| 5323 | + //we will then switch to the other thread and attempt |
| 5324 | + //to evict the parent |
| 5325 | + gdb_sync1(); |
| 5326 | + latch.count_down(); |
| 5327 | + alloc.releaseSlab(pid, cid, SlabReleaseMode::kRebalance); |
| 5328 | + gdb_sync3(); |
| 5329 | + }); |
| 5330 | + r->detach(); |
| 5331 | + |
| 5332 | + latch.wait(); |
| 5333 | + //this alloc should fail because item can't be marked for eviction |
| 5334 | + //and we exceed evict attempts |
| 5335 | + auto itemHandle = util::allocateAccessible(alloc, pid, std::to_string(10), sizes[0]); |
| 5336 | + EXPECT_EQ(itemHandle, nullptr); |
| 5337 | + //complete the move |
| 5338 | + gdb_sync2(); |
| 5339 | + auto itemHandle2 = util::allocateAccessible(alloc, pid, std::to_string(11), sizes[0]); |
| 5340 | + EXPECT_NE(itemHandle2, nullptr); |
| 5341 | + gdb_sync4(); |
| 5342 | + |
| 5343 | + } |
5192 | 5344 | // Test stats on counting chained items
|
5193 | 5345 | // 1. Alloc an item and several chained items
|
5194 | 5346 | // * Before inserting chained items, make sure count is zero
|
|
0 commit comments