1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Allocator replay tool.
5 * Author: Igor Fedotov, ifedotov@suse.com
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"
19 void usage(const string
&name
) {
20 cerr
<< "Usage: " << name
<< " <log_to_replay> <raw_duplicate|free_dump>"
24 int replay_and_check_for_duplicate(char* fname
)
26 unique_ptr
<Allocator
> alloc
;
28 FILE* f
= fopen(fname
, "r");
30 std::cerr
<< "error: unable to open " << fname
<< std::endl
;
35 bool init_done
= false;
38 interval_set
<uint64_t> owned_by_app
;
40 if (fgets(s
, sizeof(s
), f
) == nullptr) {
43 sp
= strstr(s
, "init_add_free");
45 sp
= strstr(s
, "release");
48 //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free 0x100000~680000000
50 //2019-05-30 03:23:46.780 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_add_free done
52 // 2019 - 10 - 08T16:19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release 0x450000~10000
54 // 2019 - 10 - 08T16 : 19 : 32.257 + 0300 7f5679f3fe80 10 fbmap_alloc 0x564fab96f100 release done
55 if (strstr(sp
, "done") != nullptr) {
58 std::cout
<< s
<< std::endl
;
60 std::cerr
<< "error: no allocator init before: " << s
<< std::endl
;
65 token
= strtok(nullptr, " ~");
67 offs
= strtoul(token
, nullptr, 16);
68 token
= strtok(nullptr, " ~");
70 len
= strtoul(token
, nullptr, 16);
72 std::cerr
<< "error: " << sp
<<": " << s
<< std::endl
;
75 if (!owned_by_app
.contains(offs
, len
)) {
76 std::cerr
<< "error: unexpected return to allocator, not owned by app: "
80 owned_by_app
.erase(offs
, len
);
81 if (strstr(sp
, "init_add_free") != nullptr) {
82 alloc
->init_add_free(offs
, len
);
84 PExtentVector release_set
;
85 release_set
.emplace_back(offs
, len
);
86 alloc
->release(release_set
);
90 sp
= strstr(s
, "init_rm_free");
92 //2019-05-30 03:23:46.912 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free 0x100000~680000000
94 // 2019-05-30 03:23:46.916 7f889a5edf00 10 fbmap_alloc 0x5642ed370600 init_rm_free done
96 if (strstr(sp
, "done") != nullptr) {
99 std::cout
<< s
<< std::endl
;
101 std::cerr
<< "error: no allocator init before: " << s
<< std::endl
;
106 token
= strtok(nullptr, " ~");
108 offs
= strtoul(token
, nullptr, 16);
109 token
= strtok(nullptr, " ~");
111 len
= strtoul(token
, nullptr, 16);
113 std::cerr
<< "error: " << sp
<<": " << s
<< std::endl
;
116 alloc
->init_rm_free(offs
, len
);
118 if (owned_by_app
.intersects(offs
, len
)) {
120 << "error: unexpected takeover from allocator, already owned by app: "
124 owned_by_app
.insert(offs
, len
);
129 sp
= strstr(s
, "allocate");
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
135 // Very simple and stupid check to bypass actual allocations
136 if (strstr(sp
, "~") != nullptr) {
140 std::cout
<< s
<< std::endl
;
142 std::cerr
<< "error: no allocator init before: " << s
<< std::endl
;
145 uint64_t want
, alloc_unit
;
147 token
= strtok(nullptr, " /");
149 want
= strtoul(token
, nullptr, 16);
150 token
= strtok(nullptr, " ~");
152 alloc_unit
= strtoul(token
, nullptr, 16);
153 if (want
== 0 || alloc_unit
== 0) {
154 std::cerr
<< "error: allocate: " << s
<< std::endl
;
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
;
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
;
176 std::cerr
<<"dump completed." << std::endl
;
179 owned_by_app
.insert(e
.offset
, e
.length
);
185 sp
= strstr(s
, "BitmapAllocator");
187 // 2019-05-30 03:23:43.460 7f889a5edf00 10 fbmap_alloc 0x5642ed36e900 BitmapAllocator 0x15940000000/100000
188 std::cout
<< s
<< std::endl
;
190 std::cerr
<< "error: duplicate init: " << s
<< std::endl
;
193 uint64_t total
, alloc_unit
;
195 token
= strtok(nullptr, " /");
197 total
= strtoul(token
, nullptr, 16);
198 token
= strtok(nullptr, " /");
200 alloc_unit
= strtoul(token
, nullptr, 16);
201 if (total
== 0 || alloc_unit
== 0) {
202 std::cerr
<< "error: invalid init: " << s
<< std::endl
;
205 alloc
.reset(Allocator::create(g_ceph_context
, string("bitmap"), total
,
207 owned_by_app
.insert(0, total
);
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
222 int replay_free_dump_and_apply(char* fname
,
223 std::function
<int (Allocator
*, const string
& aname
)> fn
)
227 uint64_t capacity
= 0;
228 uint64_t alloc_unit
= 0;
231 std::cout
<< "parsing..." << std::endl
;
232 bool b
= p
.parse(fname
);
234 std::cerr
<< "Failed to parse json: " << fname
<< std::endl
;
239 ceph_assert(p
.is_object());
241 auto *o
= p
.find_obj("allocator_type");
243 alloc_type
= o
->get_data_val().str
;
245 o
= p
.find_obj("allocator_name");
247 alloc_name
= o
->get_data_val().str
;
249 o
= p
.find_obj("capacity");
251 decode_json_obj(capacity
, o
);
252 o
= p
.find_obj("alloc_unit");
254 decode_json_obj(alloc_unit
, o
);
256 o
= p
.find_obj("extents");
258 ceph_assert(o
->is_array());
259 std::cout
<< "parsing completed!" << std::endl
;
261 unique_ptr
<Allocator
> alloc
;
262 alloc
.reset(Allocator::create(g_ceph_context
, alloc_type
,
263 capacity
, alloc_unit
, alloc_name
));
265 auto it
= o
->find_first();
267 auto *item_obj
= *it
;
270 string offset_str
, length_str
;
272 bool b
= JSONDecoder::decode_json("offset", offset_str
, item_obj
);
274 b
= JSONDecoder::decode_json("length", length_str
, item_obj
);
278 offset
= strtol(offset_str
.c_str(), &p
, 16);
279 length
= strtol(length_str
.c_str(), &p
, 16);
281 alloc
->init_add_free(offset
, length
);
286 int r
= fn(alloc
.get(), alloc_name
);
291 void dump_alloc(Allocator
* alloc
, const string
& aname
)
293 AdminSocket
* admin_socket
= g_ceph_context
->get_admin_socket();
294 ceph_assert(admin_socket
);
296 ceph::bufferlist in
, out
;
299 string cmd
= "{\"prefix\": \"bluestore allocator dump " + aname
+ "\"}";
300 auto r
= admin_socket
->execute_command(
304 cerr
<< "failure querying: " << cpp_strerror(r
) << std::endl
;
307 std::cout
<< std::string(out
.c_str(), out
.length()) << std::endl
;
311 int main(int argc
, char **argv
)
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);
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
) {
330 std::cout
<< "Fragmentation:" << a
->get_fragmentation()
332 std::cout
<< "Fragmentation score:" << a
->get_fragmentation_score()
334 std::cout
<< "Free:" << std::hex
<< a
->get_free() << std::dec
337 // stub to implement various testing stuff on properly initialized allocator
338 // e.g. one can dump allocator back via dump_alloc(a, aname);