]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/objectstore/allocator_replay_test.cc
8f4fe18f017e79404a07b9408ccae1a3a8ec3a59
[ceph.git] / ceph / src / test / objectstore / allocator_replay_test.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Allocator replay tool.
5 * Author: Igor Fedotov, ifedotov@suse.com
6 */
7 #include <iostream>
8
9 #include "common/ceph_argparse.h"
10 #include "common/debug.h"
11 #include "common/Cycles.h"
12 #include "common/errno.h"
13 #include "common/ceph_json.h"
14 #include "common/admin_socket.h"
15 #include "global/global_init.h"
16 #include "os/bluestore/Allocator.h"
17
18
19 void usage(const string &name) {
20 cerr << "Usage: " << name << " <log_to_replay> <raw_duplicate|free_dump>"
21 << std::endl;
22 }
23
24 int replay_and_check_for_duplicate(char* fname)
25 {
26 unique_ptr<Allocator> alloc;
27
28 FILE* f = fopen(fname, "r");
29 if (!f) {
30 std::cerr << "error: unable to open " << fname << std::endl;
31 return -1;
32 }
33
34 PExtentVector tmp;
35 bool init_done = false;
36 char s[4096];
37 char* sp, *token;
38 interval_set<uint64_t> owned_by_app;
39 while (true) {
40 if (fgets(s, sizeof(s), f) == nullptr) {
41 break;
42 }
43 sp = strstr(s, "init_add_free");
44 if (!sp) {
45 sp = strstr(s, "release");
46 }
47 if (sp) {
48 //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free 0x100000~680000000
49 // or
50 //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free done
51 // or
52 // 2019 - 10 - 08T16:19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release 0x450000~10000
53 // or
54 // 2019 - 10 - 08T16 : 19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release done
55 if (strstr(sp, "done") != nullptr) {
56 continue;
57 }
58 std::cout << s << std::endl;
59 if (!init_done) {
60 std::cerr << "error: no allocator init before: " << s << std::endl;
61 return -1;
62 }
63 uint64_t offs, len;
64 strtok(sp, " ~");
65 token = strtok(nullptr, " ~");
66 ceph_assert(token);
67 offs = strtoul(token, nullptr, 16);
68 token = strtok(nullptr, " ~");
69 ceph_assert(token);
70 len = strtoul(token, nullptr, 16);
71 if (len == 0) {
72 std::cerr << "error: " << sp <<": " << s << std::endl;
73 return -1;
74 }
75 if (!owned_by_app.contains(offs, len)) {
76 std::cerr << "error: unexpected return to allocator, not owned by app: "
77 << s << std::endl;
78 return -1;
79 }
80 owned_by_app.erase(offs, len);
81 if (strstr(sp, "init_add_free") != nullptr) {
82 alloc->init_add_free(offs, len);
83 } else {
84 PExtentVector release_set;
85 release_set.emplace_back(offs, len);
86 alloc->release(release_set);
87 }
88 continue;
89 }
90 sp = strstr(s, "init_rm_free");
91 if (sp) {
92 //2019-05-30 03:23:46.912 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free 0x100000~680000000
93 // or
94 // 2019-05-30 03:23:46.916 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free done
95
96 if (strstr(sp, "done") != nullptr) {
97 continue;
98 }
99 std::cout << s << std::endl;
100 if (!init_done) {
101 std::cerr << "error: no allocator init before: " << s << std::endl;
102 return -1;
103 }
104 uint64_t offs, len;
105 strtok(sp, " ~");
106 token = strtok(nullptr, " ~");
107 ceph_assert(token);
108 offs = strtoul(token, nullptr, 16);
109 token = strtok(nullptr, " ~");
110 ceph_assert(token);
111 len = strtoul(token, nullptr, 16);
112 if (len == 0) {
113 std::cerr << "error: " << sp <<": " << s << std::endl;
114 return -1;
115 }
116 alloc->init_rm_free(offs, len);
117
118 if (owned_by_app.intersects(offs, len)) {
119 std::cerr
120 << "error: unexpected takeover from allocator, already owned by app: "
121 << s << std::endl;
122 return -1;
123 } else {
124 owned_by_app.insert(offs, len);
125 }
126
127 continue;
128 }
129 sp = strstr(s, "allocate");
130 if (sp) {
131 //2019-05-30 03:23:48.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 allocate 0x80000000/100000,0,0
132 // and need to bypass
133 // 2019-05-30 03:23:48.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 allocate 0x69d400000~200000/100000,0,0
134
135 // Very simple and stupid check to bypass actual allocations
136 if (strstr(sp, "~") != nullptr) {
137 continue;
138 }
139
140 std::cout << s << std::endl;
141 if (!init_done) {
142 std::cerr << "error: no allocator init before: " << s << std::endl;
143 return -1;
144 }
145 uint64_t want, alloc_unit;
146 strtok(sp, " /");
147 token = strtok(nullptr, " /");
148 ceph_assert(token);
149 want = strtoul(token, nullptr, 16);
150 token = strtok(nullptr, " ~");
151 ceph_assert(token);
152 alloc_unit = strtoul(token, nullptr, 16);
153 if (want == 0 || alloc_unit == 0) {
154 std::cerr << "error: allocate: " << s << std::endl;
155 return -1;
156 }
157 tmp.clear();
158 auto allocated = alloc->allocate(want, alloc_unit, 0, 0, &tmp);
159 std::cout << "allocated TOTAL: " << allocated << std::endl;
160 for (auto& ee : tmp) {
161 std::cerr << "dump extent: " << std::hex
162 << ee.offset << "~" << ee.length
163 << std::dec << std::endl;
164 }
165 std::cerr << "dump completed." << std::endl;
166 for (auto& e : tmp) {
167 if (owned_by_app.intersects(e.offset, e.length)) {
168 std::cerr << "error: unexpected allocated extent: " << std::hex
169 << e.offset << "~" << e.length
170 << " dumping all allocations:" << std::dec << std::endl;
171 for (auto& ee : tmp) {
172 std::cerr <<"dump extent: " << std::hex
173 << ee.offset << "~" << ee.length
174 << std::dec << std::endl;
175 }
176 std::cerr <<"dump completed." << std::endl;
177 return -1;
178 } else {
179 owned_by_app.insert(e.offset, e.length);
180 }
181 }
182 continue;
183 }
184
185 sp = strstr(s, "BitmapAllocator");
186 if (sp) {
187 // 2019-05-30 03:23:43.460 7f889a5edf00 10 fbmap_alloc 0x5642ed36e900 BitmapAllocator 0x15940000000/100000
188 std::cout << s << std::endl;
189 if (init_done) {
190 std::cerr << "error: duplicate init: " << s << std::endl;
191 return -1;
192 }
193 uint64_t total, alloc_unit;
194 strtok(sp, " /");
195 token = strtok(nullptr, " /");
196 ceph_assert(token);
197 total = strtoul(token, nullptr, 16);
198 token = strtok(nullptr, " /");
199 ceph_assert(token);
200 alloc_unit = strtoul(token, nullptr, 16);
201 if (total == 0 || alloc_unit == 0) {
202 std::cerr << "error: invalid init: " << s << std::endl;
203 return -1;
204 }
205 alloc.reset(Allocator::create(g_ceph_context, string("bitmap"), total,
206 alloc_unit));
207 owned_by_app.insert(0, total);
208
209 init_done = true;
210 continue;
211 }
212 }
213 fclose(f);
214 return 0;
215 }
216
217 /*
218 * This replays allocator dump (in JSON) reported by
219 "ceph daemon <osd> bluestore allocator dump <name>"
220 command and applies custom method to it
221 */
222 int replay_free_dump_and_apply(char* fname,
223 std::function<int (Allocator*, const string& aname)> fn)
224 {
225 string alloc_type;
226 string alloc_name;
227 uint64_t capacity = 0;
228 uint64_t alloc_unit = 0;
229
230 JSONParser p;
231 std::cout << "parsing..." << std::endl;
232 bool b = p.parse(fname);
233 if (!b) {
234 std::cerr << "Failed to parse json: " << fname << std::endl;
235 return -1;
236 }
237
238 JSONObj::data_val v;
239 ceph_assert(p.is_object());
240
241 auto *o = p.find_obj("allocator_type");
242 ceph_assert(o);
243 alloc_type = o->get_data_val().str;
244
245 o = p.find_obj("allocator_name");
246 ceph_assert(o);
247 alloc_name = o->get_data_val().str;
248
249 o = p.find_obj("capacity");
250 ceph_assert(o);
251 decode_json_obj(capacity, o);
252 o = p.find_obj("alloc_unit");
253 ceph_assert(o);
254 decode_json_obj(alloc_unit, o);
255
256 o = p.find_obj("extents");
257 ceph_assert(o);
258 ceph_assert(o->is_array());
259 std::cout << "parsing completed!" << std::endl;
260
261 unique_ptr<Allocator> alloc;
262 alloc.reset(Allocator::create(g_ceph_context, alloc_type,
263 capacity, alloc_unit, alloc_name));
264
265 auto it = o->find_first();
266 while (!it.end()) {
267 auto *item_obj = *it;
268 uint64_t offset = 0;
269 uint64_t length = 0;
270 string offset_str, length_str;
271
272 bool b = JSONDecoder::decode_json("offset", offset_str, item_obj);
273 ceph_assert(b);
274 b = JSONDecoder::decode_json("length", length_str, item_obj);
275 ceph_assert(b);
276
277 char* p;
278 offset = strtol(offset_str.c_str(), &p, 16);
279 length = strtol(length_str.c_str(), &p, 16);
280
281 alloc->init_add_free(offset, length);
282
283 ++it;
284 }
285
286 int r = fn(alloc.get(), alloc_name);
287
288 return r;
289 }
290
291 void dump_alloc(Allocator* alloc, const string& aname)
292 {
293 AdminSocket* admin_socket = g_ceph_context->get_admin_socket();
294 ceph_assert(admin_socket);
295
296 ceph::bufferlist in, out;
297 ostringstream err;
298
299 string cmd = "{\"prefix\": \"bluestore allocator dump " + aname + "\"}";
300 auto r = admin_socket->execute_command(
301 { cmd },
302 in, err, &out);
303 if (r != 0) {
304 cerr << "failure querying: " << cpp_strerror(r) << std::endl;
305 }
306 else {
307 std::cout << std::string(out.c_str(), out.length()) << std::endl;
308 }
309 }
310
311 int main(int argc, char **argv)
312 {
313 vector<const char*> args;
314 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
315 CODE_ENVIRONMENT_UTILITY,
316 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
317 common_init_finish(g_ceph_context);
318 g_ceph_context->_conf.apply_changes(nullptr);
319
320 if (argc < 3) {
321 usage(argv[0]);
322 return 1;
323 }
324 if (strcmp(argv[2], "raw_duplicate") == 0) {
325 return replay_and_check_for_duplicate(argv[1]);
326 } else if (strcmp(argv[2], "free_dump") == 0) {
327 return replay_free_dump_and_apply(argv[1],
328 [&](Allocator* a, const string& aname) {
329 ceph_assert(a);
330 std::cout << "Fragmentation:" << a->get_fragmentation()
331 << std::endl;
332 std::cout << "Fragmentation score:" << a->get_fragmentation_score()
333 << std::endl;
334 std::cout << "Free:" << std::hex << a->get_free() << std::dec
335 << std::endl;
336 {
337 // stub to implement various testing stuff on properly initialized allocator
338 // e.g. one can dump allocator back via dump_alloc(a, aname);
339 }
340 return 0;
341 });
342 }
343 }