/src/rocksdb/env/file_system_tracer.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2019-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | |
6 | | #pragma once |
7 | | |
8 | | #include "rocksdb/file_system.h" |
9 | | #include "rocksdb/system_clock.h" |
10 | | #include "trace_replay/io_tracer.h" |
11 | | |
12 | | namespace ROCKSDB_NAMESPACE { |
13 | | |
14 | | // FileSystemTracingWrapper is a wrapper class above FileSystem that forwards |
15 | | // the call to the underlying storage system. It then invokes IOTracer to record |
16 | | // file operations and other contextual information in a binary format for |
17 | | // tracing. It overrides methods we are interested in tracing and extends |
18 | | // FileSystemWrapper, which forwards all methods that are not explicitly |
19 | | // overridden. |
20 | | class FileSystemTracingWrapper : public FileSystemWrapper { |
21 | | public: |
22 | | FileSystemTracingWrapper(const std::shared_ptr<FileSystem>& t, |
23 | | const std::shared_ptr<IOTracer>& io_tracer) |
24 | 180k | : FileSystemWrapper(t), |
25 | 180k | io_tracer_(io_tracer), |
26 | 180k | clock_(SystemClock::Default().get()) {} |
27 | | |
28 | 180k | ~FileSystemTracingWrapper() override {} |
29 | | |
30 | 0 | static const char* kClassName() { return "FileSystemTracing"; } |
31 | 0 | const char* Name() const override { return kClassName(); } |
32 | | |
33 | | IOStatus NewSequentialFile(const std::string& fname, |
34 | | const FileOptions& file_opts, |
35 | | std::unique_ptr<FSSequentialFile>* result, |
36 | | IODebugContext* dbg) override; |
37 | | |
38 | | IOStatus NewRandomAccessFile(const std::string& fname, |
39 | | const FileOptions& file_opts, |
40 | | std::unique_ptr<FSRandomAccessFile>* result, |
41 | | IODebugContext* dbg) override; |
42 | | |
43 | | IOStatus NewWritableFile(const std::string& fname, |
44 | | const FileOptions& file_opts, |
45 | | std::unique_ptr<FSWritableFile>* result, |
46 | | IODebugContext* dbg) override; |
47 | | |
48 | | IOStatus ReopenWritableFile(const std::string& fname, |
49 | | const FileOptions& file_opts, |
50 | | std::unique_ptr<FSWritableFile>* result, |
51 | | IODebugContext* dbg) override; |
52 | | |
53 | | IOStatus ReuseWritableFile(const std::string& fname, |
54 | | const std::string& old_fname, |
55 | | const FileOptions& file_opts, |
56 | | std::unique_ptr<FSWritableFile>* result, |
57 | | IODebugContext* dbg) override; |
58 | | |
59 | | IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options, |
60 | | std::unique_ptr<FSRandomRWFile>* result, |
61 | | IODebugContext* dbg) override; |
62 | | |
63 | | IOStatus NewDirectory(const std::string& name, const IOOptions& io_opts, |
64 | | std::unique_ptr<FSDirectory>* result, |
65 | | IODebugContext* dbg) override; |
66 | | |
67 | | IOStatus GetChildren(const std::string& dir, const IOOptions& io_opts, |
68 | | std::vector<std::string>* r, |
69 | | IODebugContext* dbg) override; |
70 | | |
71 | | IOStatus DeleteFile(const std::string& fname, const IOOptions& options, |
72 | | IODebugContext* dbg) override; |
73 | | |
74 | | IOStatus CreateDir(const std::string& dirname, const IOOptions& options, |
75 | | IODebugContext* dbg) override; |
76 | | |
77 | | IOStatus CreateDirIfMissing(const std::string& dirname, |
78 | | const IOOptions& options, |
79 | | IODebugContext* dbg) override; |
80 | | |
81 | | IOStatus DeleteDir(const std::string& dirname, const IOOptions& options, |
82 | | IODebugContext* dbg) override; |
83 | | |
84 | | IOStatus GetFileSize(const std::string& fname, const IOOptions& options, |
85 | | uint64_t* file_size, IODebugContext* dbg) override; |
86 | | |
87 | | IOStatus Truncate(const std::string& fname, size_t size, |
88 | | const IOOptions& options, IODebugContext* dbg) override; |
89 | | |
90 | | private: |
91 | | std::shared_ptr<IOTracer> io_tracer_; |
92 | | SystemClock* clock_; |
93 | | }; |
94 | | |
95 | | // The FileSystemPtr is a wrapper class that takes pointer to storage systems |
96 | | // (such as posix filesystems). It overloads operator -> and returns a pointer |
97 | | // of either FileSystem or FileSystemTracingWrapper based on whether tracing is |
98 | | // enabled or not. It is added to bypass FileSystemTracingWrapper when tracing |
99 | | // is disabled. |
100 | | class FileSystemPtr { |
101 | | public: |
102 | | FileSystemPtr(std::shared_ptr<FileSystem> fs, |
103 | | const std::shared_ptr<IOTracer>& io_tracer) |
104 | 180k | : fs_(fs), io_tracer_(io_tracer) { |
105 | 180k | fs_tracer_ = std::make_shared<FileSystemTracingWrapper>(fs_, io_tracer_); |
106 | 180k | } |
107 | | |
108 | 934k | std::shared_ptr<FileSystem> operator->() const { |
109 | 934k | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
110 | 0 | return fs_tracer_; |
111 | 934k | } else { |
112 | 934k | return fs_; |
113 | 934k | } |
114 | 934k | } |
115 | | |
116 | | /* Returns the underlying File System pointer */ |
117 | 467k | FileSystem* get() const { |
118 | 467k | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
119 | 0 | return fs_tracer_.get(); |
120 | 467k | } else { |
121 | 467k | return fs_.get(); |
122 | 467k | } |
123 | 467k | } |
124 | | |
125 | | private: |
126 | | std::shared_ptr<FileSystem> fs_; |
127 | | std::shared_ptr<IOTracer> io_tracer_; |
128 | | std::shared_ptr<FileSystemTracingWrapper> fs_tracer_; |
129 | | }; |
130 | | |
131 | | // FSSequentialFileTracingWrapper is a wrapper class above FSSequentialFile that |
132 | | // forwards the call to the underlying storage system. It then invokes IOTracer |
133 | | // to record file operations and other contextual information in a binary format |
134 | | // for tracing. It overrides methods we are interested in tracing and extends |
135 | | // FSSequentialFileWrapper, which forwards all methods that are not explicitly |
136 | | // overridden. |
137 | | class FSSequentialFileTracingWrapper : public FSSequentialFileOwnerWrapper { |
138 | | public: |
139 | | FSSequentialFileTracingWrapper(std::unique_ptr<FSSequentialFile>&& t, |
140 | | std::shared_ptr<IOTracer> io_tracer, |
141 | | const std::string& file_name) |
142 | 208k | : FSSequentialFileOwnerWrapper(std::move(t)), |
143 | 208k | io_tracer_(io_tracer), |
144 | 208k | clock_(SystemClock::Default().get()), |
145 | 208k | file_name_(file_name) {} |
146 | | |
147 | 208k | ~FSSequentialFileTracingWrapper() override {} |
148 | | |
149 | | IOStatus Read(size_t n, const IOOptions& options, Slice* result, |
150 | | char* scratch, IODebugContext* dbg) override; |
151 | | |
152 | | IOStatus InvalidateCache(size_t offset, size_t length) override; |
153 | | |
154 | | IOStatus PositionedRead(uint64_t offset, size_t n, const IOOptions& options, |
155 | | Slice* result, char* scratch, |
156 | | IODebugContext* dbg) override; |
157 | | |
158 | | private: |
159 | | std::shared_ptr<IOTracer> io_tracer_; |
160 | | SystemClock* clock_; |
161 | | std::string file_name_; |
162 | | }; |
163 | | |
164 | | // The FSSequentialFilePtr is a wrapper class that takes pointer to storage |
165 | | // systems (such as posix filesystems). It overloads operator -> and returns a |
166 | | // pointer of either FSSequentialFile or FSSequentialFileTracingWrapper based on |
167 | | // whether tracing is enabled or not. It is added to bypass |
168 | | // FSSequentialFileTracingWrapper when tracing is disabled. |
169 | | class FSSequentialFilePtr { |
170 | | public: |
171 | | FSSequentialFilePtr() = delete; |
172 | | FSSequentialFilePtr(std::unique_ptr<FSSequentialFile>&& fs, |
173 | | const std::shared_ptr<IOTracer>& io_tracer, |
174 | | const std::string& file_name) |
175 | 208k | : io_tracer_(io_tracer), |
176 | 208k | fs_tracer_(std::move(fs), io_tracer_, |
177 | 208k | file_name.substr(file_name.find_last_of("/\\") + |
178 | 208k | 1) /* pass file name */) {} |
179 | | |
180 | 517k | FSSequentialFile* operator->() const { |
181 | 517k | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
182 | 0 | return const_cast<FSSequentialFileTracingWrapper*>(&fs_tracer_); |
183 | 517k | } else { |
184 | 517k | return fs_tracer_.target(); |
185 | 517k | } |
186 | 517k | } |
187 | | |
188 | 0 | FSSequentialFile* get() const { |
189 | 0 | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
190 | 0 | return const_cast<FSSequentialFileTracingWrapper*>(&fs_tracer_); |
191 | 0 | } else { |
192 | 0 | return fs_tracer_.target(); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | private: |
197 | | std::shared_ptr<IOTracer> io_tracer_; |
198 | | FSSequentialFileTracingWrapper fs_tracer_; |
199 | | }; |
200 | | |
201 | | // FSRandomAccessFileTracingWrapper is a wrapper class above FSRandomAccessFile |
202 | | // that forwards the call to the underlying storage system. It then invokes |
203 | | // IOTracer to record file operations and other contextual information in a |
204 | | // binary format for tracing. It overrides methods we are interested in tracing |
205 | | // and extends FSRandomAccessFileWrapper, which forwards all methods that are |
206 | | // not explicitly overridden. |
207 | | class FSRandomAccessFileTracingWrapper : public FSRandomAccessFileOwnerWrapper { |
208 | | public: |
209 | | FSRandomAccessFileTracingWrapper(std::unique_ptr<FSRandomAccessFile>&& t, |
210 | | std::shared_ptr<IOTracer> io_tracer, |
211 | | const std::string& file_name) |
212 | 125k | : FSRandomAccessFileOwnerWrapper(std::move(t)), |
213 | 125k | io_tracer_(io_tracer), |
214 | 125k | clock_(SystemClock::Default().get()), |
215 | 125k | file_name_(file_name) {} |
216 | | |
217 | 126k | ~FSRandomAccessFileTracingWrapper() override {} |
218 | | |
219 | | IOStatus Read(uint64_t offset, size_t n, const IOOptions& options, |
220 | | Slice* result, char* scratch, |
221 | | IODebugContext* dbg) const override; |
222 | | |
223 | | IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs, |
224 | | const IOOptions& options, IODebugContext* dbg) override; |
225 | | |
226 | | IOStatus Prefetch(uint64_t offset, size_t n, const IOOptions& options, |
227 | | IODebugContext* dbg) override; |
228 | | |
229 | | IOStatus InvalidateCache(size_t offset, size_t length) override; |
230 | | |
231 | | IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts, |
232 | | std::function<void(FSReadRequest&, void*)> cb, |
233 | | void* cb_arg, void** io_handle, IOHandleDeleter* del_fn, |
234 | | IODebugContext* dbg) override; |
235 | | |
236 | | void ReadAsyncCallback(FSReadRequest& req, void* cb_arg); |
237 | | |
238 | | private: |
239 | | std::shared_ptr<IOTracer> io_tracer_; |
240 | | SystemClock* clock_; |
241 | | // Stores file name instead of full path. |
242 | | std::string file_name_; |
243 | | |
244 | | struct ReadAsyncCallbackInfo { |
245 | | uint64_t start_time_; |
246 | | std::function<void(FSReadRequest&, void*)> cb_; |
247 | | void* cb_arg_; |
248 | | std::string file_op_; |
249 | | }; |
250 | | }; |
251 | | |
252 | | // The FSRandomAccessFilePtr is a wrapper class that takes pointer to storage |
253 | | // systems (such as posix filesystems). It overloads operator -> and returns a |
254 | | // pointer of either FSRandomAccessFile or FSRandomAccessFileTracingWrapper |
255 | | // based on whether tracing is enabled or not. It is added to bypass |
256 | | // FSRandomAccessFileTracingWrapper when tracing is disabled. |
257 | | class FSRandomAccessFilePtr { |
258 | | public: |
259 | | FSRandomAccessFilePtr(std::unique_ptr<FSRandomAccessFile>&& fs, |
260 | | const std::shared_ptr<IOTracer>& io_tracer, |
261 | | const std::string& file_name) |
262 | 125k | : io_tracer_(io_tracer), |
263 | 125k | fs_tracer_(std::move(fs), io_tracer_, |
264 | 125k | file_name.substr(file_name.find_last_of("/\\") + |
265 | 125k | 1) /* pass file name */) {} |
266 | | |
267 | 2.41M | FSRandomAccessFile* operator->() const { |
268 | 2.41M | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
269 | 0 | return const_cast<FSRandomAccessFileTracingWrapper*>(&fs_tracer_); |
270 | 2.41M | } else { |
271 | 2.41M | return fs_tracer_.target(); |
272 | 2.41M | } |
273 | 2.41M | } |
274 | | |
275 | 125k | FSRandomAccessFile* get() const { |
276 | 125k | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
277 | 0 | return const_cast<FSRandomAccessFileTracingWrapper*>(&fs_tracer_); |
278 | 125k | } else { |
279 | 125k | return fs_tracer_.target(); |
280 | 125k | } |
281 | 125k | } |
282 | | |
283 | | private: |
284 | | std::shared_ptr<IOTracer> io_tracer_; |
285 | | FSRandomAccessFileTracingWrapper fs_tracer_; |
286 | | }; |
287 | | |
288 | | // FSWritableFileTracingWrapper is a wrapper class above FSWritableFile that |
289 | | // forwards the call to the underlying storage system. It then invokes IOTracer |
290 | | // to record file operations and other contextual information in a binary format |
291 | | // for tracing. It overrides methods we are interested in tracing and extends |
292 | | // FSWritableFileWrapper, which forwards all methods that are not explicitly |
293 | | // overridden. |
294 | | class FSWritableFileTracingWrapper : public FSWritableFileOwnerWrapper { |
295 | | public: |
296 | | FSWritableFileTracingWrapper(std::unique_ptr<FSWritableFile>&& t, |
297 | | std::shared_ptr<IOTracer> io_tracer, |
298 | | const std::string& file_name) |
299 | 324k | : FSWritableFileOwnerWrapper(std::move(t)), |
300 | 324k | io_tracer_(io_tracer), |
301 | 324k | clock_(SystemClock::Default().get()), |
302 | 324k | file_name_(file_name) {} |
303 | | |
304 | 324k | ~FSWritableFileTracingWrapper() override {} |
305 | | |
306 | | IOStatus Append(const Slice& data, const IOOptions& options, |
307 | | IODebugContext* dbg) override; |
308 | | IOStatus Append(const Slice& data, const IOOptions& options, |
309 | | const DataVerificationInfo& /*verification_info*/, |
310 | 0 | IODebugContext* dbg) override { |
311 | 0 | return Append(data, options, dbg); |
312 | 0 | } |
313 | | |
314 | | IOStatus PositionedAppend(const Slice& data, uint64_t offset, |
315 | | const IOOptions& options, |
316 | | IODebugContext* dbg) override; |
317 | | IOStatus PositionedAppend(const Slice& data, uint64_t offset, |
318 | | const IOOptions& options, |
319 | | const DataVerificationInfo& /*verification_info*/, |
320 | 0 | IODebugContext* dbg) override { |
321 | 0 | return PositionedAppend(data, offset, options, dbg); |
322 | 0 | } |
323 | | |
324 | | IOStatus Truncate(uint64_t size, const IOOptions& options, |
325 | | IODebugContext* dbg) override; |
326 | | |
327 | | IOStatus Close(const IOOptions& options, IODebugContext* dbg) override; |
328 | | |
329 | | uint64_t GetFileSize(const IOOptions& options, IODebugContext* dbg) override; |
330 | | |
331 | | IOStatus InvalidateCache(size_t offset, size_t length) override; |
332 | | |
333 | | private: |
334 | | std::shared_ptr<IOTracer> io_tracer_; |
335 | | SystemClock* clock_; |
336 | | // Stores file name instead of full path. |
337 | | std::string file_name_; |
338 | | }; |
339 | | |
340 | | // The FSWritableFilePtr is a wrapper class that takes pointer to storage |
341 | | // systems (such as posix filesystems). It overloads operator -> and returns a |
342 | | // pointer of either FSWritableFile or FSWritableFileTracingWrapper based on |
343 | | // whether tracing is enabled or not. It is added to bypass |
344 | | // FSWritableFileTracingWrapper when tracing is disabled. |
345 | | class FSWritableFilePtr { |
346 | | public: |
347 | | FSWritableFilePtr(std::unique_ptr<FSWritableFile>&& fs, |
348 | | const std::shared_ptr<IOTracer>& io_tracer, |
349 | | const std::string& file_name) |
350 | 324k | : io_tracer_(io_tracer) { |
351 | 324k | fs_tracer_.reset(new FSWritableFileTracingWrapper( |
352 | 324k | std::move(fs), io_tracer_, |
353 | 324k | file_name.substr(file_name.find_last_of("/\\") + |
354 | 324k | 1) /* pass file name */)); |
355 | 324k | } |
356 | | |
357 | 141M | FSWritableFile* operator->() const { |
358 | 141M | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
359 | 0 | return fs_tracer_.get(); |
360 | 141M | } else { |
361 | 141M | return fs_tracer_->target(); |
362 | 141M | } |
363 | 141M | } |
364 | | |
365 | 35.4M | FSWritableFile* get() const { |
366 | 35.4M | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
367 | 0 | return fs_tracer_.get(); |
368 | 35.4M | } else if (fs_tracer_) { |
369 | 34.8M | return fs_tracer_->target(); |
370 | 34.8M | } else { |
371 | 513k | return nullptr; |
372 | 513k | } |
373 | 35.4M | } |
374 | | |
375 | 324k | void reset() { |
376 | 324k | fs_tracer_.reset(); |
377 | 324k | io_tracer_ = nullptr; |
378 | 324k | } |
379 | | |
380 | | private: |
381 | | std::shared_ptr<IOTracer> io_tracer_; |
382 | | std::unique_ptr<FSWritableFileTracingWrapper> fs_tracer_; |
383 | | }; |
384 | | |
385 | | // FSRandomRWFileTracingWrapper is a wrapper class above FSRandomRWFile that |
386 | | // forwards the call to the underlying storage system. It then invokes IOTracer |
387 | | // to record file operations and other contextual information in a binary format |
388 | | // for tracing. It overrides methods we are interested in tracing and extends |
389 | | // FSRandomRWFileWrapper, which forwards all methods that are not explicitly |
390 | | // overridden. |
391 | | class FSRandomRWFileTracingWrapper : public FSRandomRWFileOwnerWrapper { |
392 | | public: |
393 | | FSRandomRWFileTracingWrapper(std::unique_ptr<FSRandomRWFile>&& t, |
394 | | std::shared_ptr<IOTracer> io_tracer, |
395 | | const std::string& file_name) |
396 | 0 | : FSRandomRWFileOwnerWrapper(std::move(t)), |
397 | 0 | io_tracer_(io_tracer), |
398 | 0 | clock_(SystemClock::Default().get()), |
399 | 0 | file_name_(file_name) {} |
400 | | |
401 | 0 | ~FSRandomRWFileTracingWrapper() override {} |
402 | | |
403 | | IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options, |
404 | | IODebugContext* dbg) override; |
405 | | |
406 | | IOStatus Read(uint64_t offset, size_t n, const IOOptions& options, |
407 | | Slice* result, char* scratch, |
408 | | IODebugContext* dbg) const override; |
409 | | |
410 | | IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override; |
411 | | |
412 | | IOStatus Close(const IOOptions& options, IODebugContext* dbg) override; |
413 | | |
414 | | IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override; |
415 | | |
416 | | IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override; |
417 | | |
418 | | private: |
419 | | std::shared_ptr<IOTracer> io_tracer_; |
420 | | SystemClock* clock_; |
421 | | // Stores file name instead of full path. |
422 | | std::string file_name_; |
423 | | }; |
424 | | |
425 | | // The FSRandomRWFilePtr is a wrapper class that takes pointer to storage |
426 | | // systems (such as posix filesystems). It overloads operator -> and returns a |
427 | | // pointer of either FSRandomRWFile or FSRandomRWFileTracingWrapper based on |
428 | | // whether tracing is enabled or not. It is added to bypass |
429 | | // FSRandomRWFileTracingWrapper when tracing is disabled. |
430 | | class FSRandomRWFilePtr { |
431 | | public: |
432 | | FSRandomRWFilePtr(std::unique_ptr<FSRandomRWFile>&& fs, |
433 | | std::shared_ptr<IOTracer> io_tracer, |
434 | | const std::string& file_name) |
435 | 0 | : io_tracer_(io_tracer), |
436 | 0 | fs_tracer_(std::move(fs), io_tracer_, |
437 | 0 | file_name.substr(file_name.find_last_of("/\\") + |
438 | 0 | 1) /* pass file name */) {} |
439 | | |
440 | 0 | FSRandomRWFile* operator->() const { |
441 | 0 | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
442 | 0 | return const_cast<FSRandomRWFileTracingWrapper*>(&fs_tracer_); |
443 | 0 | } else { |
444 | 0 | return fs_tracer_.target(); |
445 | 0 | } |
446 | 0 | } |
447 | | |
448 | 0 | FSRandomRWFile* get() const { |
449 | 0 | if (io_tracer_ && io_tracer_->is_tracing_enabled()) { |
450 | 0 | return const_cast<FSRandomRWFileTracingWrapper*>(&fs_tracer_); |
451 | 0 | } else { |
452 | 0 | return fs_tracer_.target(); |
453 | 0 | } |
454 | 0 | } |
455 | | |
456 | | private: |
457 | | std::shared_ptr<IOTracer> io_tracer_; |
458 | | FSRandomRWFileTracingWrapper fs_tracer_; |
459 | | }; |
460 | | |
461 | | } // namespace ROCKSDB_NAMESPACE |