From d9aa77225d5813dd1dd9adec16b22064d7b999aa Mon Sep 17 00:00:00 2001 From: Kellen Renshaw Date: Wed, 27 Oct 2021 22:32:20 +0000 Subject: [PATCH] test/allocator_replay_test: Add replay_alloc option New replay_alloc option added to ceph_test_alloc_replay binary that permits the loading of a dump produced by "ceph daemon osd. bluestore allocator dump block" and replaying a list of allocation requests against the loaded allocator state an arbitrary number of times (default, 100). Once the allocator dump is loaded, the fragmentation state and free space information are printed. Then the list of allocation requests is replayed against that state. Output consists of the time in ns that the allocator took to return the requested allocation, along with the request. If an allocation request fails or the list of allocation requests is completed, the error info, if any, and the fragementation and free space information is printed. The list of allocations is formatted as a file with one allocation request per line, with space separated values for "want", "unit", "max", and "hint". Values can be any integer format supported by std::scanf()'s %u formatter. The allocation request line format: [max] [hint] Example request lines: 0x4000 0x4000 0x4000 0x0 0x18000 0x4000 0x18000 0x0 The "want" and "unit" values are required. Optional request values are 0 if not present in the request. Fixes: https://tracker.ceph.com/issues/53571 Signed-off-by: Kellen Renshaw --- src/test/objectstore/allocator_replay_test.cc | 104 +++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/src/test/objectstore/allocator_replay_test.cc b/src/test/objectstore/allocator_replay_test.cc index 811cc92cdeaf4..401eaa42703e4 100644 --- a/src/test/objectstore/allocator_replay_test.cc +++ b/src/test/objectstore/allocator_replay_test.cc @@ -18,8 +18,14 @@ using namespace std; void usage(const string &name) { - cerr << "Usage: " << name << " " - << std::endl; + cerr << "Usage: " << name << " " << std::endl; +} + +void usage_replay_alloc(const string &name) { + cerr << "Detailed replay_alloc usage: " << name << " replay_alloc [number of replays]" << std::endl; + cerr << "The number of replays defaults to 1." << std::endl; + cerr << "The \"alloc_list_file\" parameter should be a file with allocation requests, one per line." << std::endl; + cerr << "Allocation request format (space separated, optional parameters are 0 if not given): want unit [max] [hint]" << std::endl; } int replay_and_check_for_duplicate(char* fname) @@ -389,5 +395,99 @@ int main(int argc, char **argv) << ", unit:" << alloc_unit << std::endl; return 0; }); + } else if (strcmp(argv[2], "replay_alloc") == 0) { + if (argc < 4) { + std::cerr << "Error: insufficient arguments for \"replay_alloc\" option." + << std::endl; + usage_replay_alloc(argv[0]); + return 1; + } + return replay_free_dump_and_apply(argv[1], + [&](Allocator *a, const string &aname) { + ceph_assert(a); + std::cout << "Fragmentation:" << a->get_fragmentation() + << std::endl; + std::cout << "Fragmentation score:" << a->get_fragmentation_score() + << std::endl; + std::cout << "Free:" << std::hex << a->get_free() << std::dec + << std::endl; + { + /* replay a set of allocation requests */ + char s[4096]; + + FILE *f_alloc_list = fopen(argv[3], "r"); + if (!f_alloc_list) { + std::cerr << "error: unable to open " << argv[3] << std::endl; + return -1; + } + + /* Replay user specified number of times to simulate extended activity + * Defaults to 1 replay. + */ + auto replay_count = 1; + if (argc == 5) { + replay_count = atoi(argv[4]); + } + + for (auto i = 0; i < replay_count; ++i) { + while (fgets(s, sizeof(s), f_alloc_list) != nullptr) { + /* parse allocation request */ + uint64_t want = 0, unit = 0, max = 0, hint = 0; + + if (std::sscanf(s, "%ji %ji %ji %ji", &want, &unit, &max, &hint) < 2) + { + cerr << "Error: malformed allocation request:" << std::endl; + cerr << s << std::endl; + /* do not attempt to allocate a malformed request */ + continue; + } + + /* timestamp for allocation start */ + auto t0 = ceph::mono_clock::now(); + + /* allocate */ + PExtentVector extents; + auto r = a->allocate(want, unit, max, hint, &extents); + if (r < 0) { + /* blind replays of allocations may run out of space, provide info for easy confirmation */ + std::cerr << "Error: allocation failure code: " << r + << " requested want/unit/max/hint (hex): " << std::hex + << want << "/" << unit << "/" << max << "/" << hint + << std::dec << std::endl; + std::cerr << "Fragmentation:" << a->get_fragmentation() + << std::endl; + std::cerr << "Fragmentation score:" << a->get_fragmentation_score() + << std::endl; + std::cerr << "Free:" << std::hex << a->get_free() << std::dec + << std::endl; + /* return 0 if the allocator ran out of space */ + if (r == -ENOSPC) { + return 0; + } + return -1; + } + + /* Outputs the allocation's duration in nanoseconds and the allocation request parameters */ + std::cout << "Duration (ns): " << (ceph::mono_clock::now() - t0).count() + << " want/unit/max/hint (hex): " << std::hex + << want << "/" << unit << "/" << max << "/" << hint + << std::dec << std::endl; + + /* Do not release. */ + //alloc->release(extents); + extents.clear(); + } + fseek(f_alloc_list, 0, SEEK_SET); + } + fclose(f_alloc_list); + std::cout << "Fragmentation:" << a->get_fragmentation() + << std::endl; + std::cout << "Fragmentation score:" << a->get_fragmentation_score() + << std::endl; + std::cout << "Free:" << std::hex << a->get_free() << std::dec + << std::endl; + } + return 0; + }); } } -- 2.39.5