Line data Source code
1 : /*
2 : Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
3 : Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
4 :
5 : This software was partially supported by the
6 : EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
7 :
8 : This software was partially supported by the
9 : ADA-FS project under the SPPEXA project funded by the DFG.
10 :
11 : This file is part of GekkoFS' POSIX interface.
12 :
13 : GekkoFS' POSIX interface is free software: you can redistribute it and/or
14 : modify it under the terms of the GNU Lesser General Public License as
15 : published by the Free Software Foundation, either version 3 of the License,
16 : or (at your option) any later version.
17 :
18 : GekkoFS' POSIX interface is distributed in the hope that it will be useful,
19 : but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : GNU Lesser General Public License for more details.
22 :
23 : You should have received a copy of the GNU Lesser General Public License
24 : along with GekkoFS' POSIX interface. If not, see
25 : <https://www.gnu.org/licenses/>.
26 :
27 : SPDX-License-Identifier: LGPL-3.0-or-later
28 : */
29 :
30 : #ifndef LIBGKFS_LOGGING_HPP
31 : #define LIBGKFS_LOGGING_HPP
32 :
33 : #ifndef BYPASS_SYSCALL
34 : #include <libsyscall_intercept_hook_point.h>
35 : #else
36 : #include <client/void_syscall_intercept.hpp>
37 : #endif
38 :
39 : #include <type_traits>
40 : #include <client/make_array.hpp>
41 : #include <client/syscalls.hpp>
42 : #include <optional>
43 : #include <fmt/format.h>
44 : #include <fmt/ostream.h>
45 : #include <hermes.hpp>
46 :
47 : #ifdef GKFS_DEBUG_BUILD
48 : #include <bitset>
49 : #endif
50 :
51 : namespace gkfs::log {
52 :
53 : enum class log_level : unsigned int {
54 : print_syscalls = 1 << 0,
55 : print_syscalls_entry = 1 << 1,
56 : print_info = 1 << 2,
57 : print_critical = 1 << 3,
58 : print_errors = 1 << 4,
59 : print_warnings = 1 << 5,
60 : print_hermes = 1 << 6,
61 : print_mercury = 1 << 7,
62 : print_debug = 1 << 8,
63 : print_trace_reads = 1 << 9,
64 :
65 : // for internal use
66 : print_none = 0,
67 : print_all = print_syscalls | print_syscalls_entry | print_info |
68 : print_critical | print_errors | print_warnings | print_hermes |
69 : print_mercury | print_debug,
70 : print_most = print_all & ~print_syscalls_entry,
71 : print_help = 1 << 10
72 : };
73 :
74 : inline constexpr log_level
75 30044199 : operator&(log_level l1, log_level l2) {
76 30044199 : return log_level(static_cast<short>(l1) & static_cast<short>(l2));
77 : }
78 :
79 : inline constexpr log_level
80 269 : operator|(log_level l1, log_level l2) {
81 269 : return log_level(static_cast<short>(l1) | static_cast<short>(l2));
82 : }
83 :
84 : inline constexpr log_level
85 : operator^(log_level l1, log_level l2) {
86 : return log_level(static_cast<short>(l1) ^ static_cast<short>(l2));
87 : }
88 :
89 : inline constexpr log_level
90 : operator~(log_level l1) {
91 : return log_level(~static_cast<short>(l1));
92 : }
93 :
94 : inline constexpr bool
95 30044199 : operator!(log_level dm) {
96 30005311 : return static_cast<short>(dm) == 0;
97 : }
98 :
99 : inline const log_level&
100 269 : operator|=(log_level& l1, log_level l2) {
101 269 : return l1 = l1 | l2;
102 : }
103 :
104 : inline const log_level&
105 : operator&=(log_level& l1, log_level l2) {
106 : return l1 = l1 & l2;
107 : }
108 :
109 : inline const log_level&
110 : operator^=(log_level& l1, log_level l2) {
111 : return l1 = l1 ^ l2;
112 : }
113 :
114 :
115 : static const auto constexpr syscall = log_level::print_syscalls;
116 : static const auto constexpr syscall_at_entry = log_level::print_syscalls_entry;
117 : static const auto constexpr info = log_level::print_info;
118 : static const auto constexpr critical = log_level::print_critical;
119 : static const auto constexpr error = log_level::print_errors;
120 : static const auto constexpr warning = log_level::print_warnings;
121 : static const auto constexpr hermes = log_level::print_hermes;
122 : static const auto constexpr mercury = log_level::print_mercury;
123 : static const auto constexpr debug = log_level::print_debug;
124 : static const auto constexpr trace_reads = log_level::print_trace_reads;
125 : static const auto constexpr none = log_level::print_none;
126 : static const auto constexpr most = log_level::print_most;
127 : static const auto constexpr all = log_level::print_all;
128 : static const auto constexpr help = log_level::print_help;
129 :
130 : static const auto constexpr level_names = utils::make_array(
131 : "syscall",
132 : "syscall", // sycall_entry uses the same name as syscall
133 : "info", "critical", "error", "warning", "hermes", "mercury", "debug",
134 : "trace_reads");
135 :
136 : inline constexpr auto
137 480058 : lookup_level_name(log_level l) {
138 :
139 480058 : assert(l != log::none && l != log::help);
140 :
141 : // since all log levels are powers of 2, we can find a name
142 : // very efficiently by counting the number of trailing 0-bits in l
143 480058 : const auto i = __builtin_ctz(static_cast<short>(l));
144 480058 : assert(i >= 0 && static_cast<std::size_t>(i) < level_names.size());
145 :
146 480058 : return level_names.at(i);
147 : }
148 :
149 :
150 : // forward declaration
151 : struct logger;
152 :
153 : namespace detail {
154 :
155 : template <typename Buffer>
156 : static inline void
157 : log_buffer(std::FILE* fp, Buffer&& buffer) {
158 : log_buffer(::fileno(fp), std::forward<Buffer>(buffer));
159 : }
160 :
161 : template <typename Buffer>
162 : static inline void
163 480058 : log_buffer(int fd, Buffer&& buffer) {
164 :
165 480058 : if(fd < 0) {
166 0 : throw std::runtime_error("Invalid file descriptor");
167 : }
168 :
169 480058 : ::syscall_no_intercept(SYS_write, fd, buffer.data(), buffer.size());
170 480059 : }
171 :
172 : static inline void
173 : log_buffer(int fd, const void* buffer, std::size_t length) {
174 : if(fd < 0) {
175 : throw std::runtime_error("Invalid file descriptor");
176 : }
177 :
178 : ::syscall_no_intercept(SYS_write, fd, buffer, length);
179 : }
180 :
181 : /**
182 : * @brief convert a time_t to a tm
183 : * It is not POSIX compliant, but it dows not uses any syscall or timezone
184 : * Converts a Unix timestamp (number of seconds since the beginning of 1970
185 : * CE) to a Gregorian civil date-time tuple in GMT (UTC) time zone.
186 : *
187 : * This conforms to C89 (and C99...) and POSIX.
188 : *
189 : * This implementation works, and doesn't overflow for any sizeof(time_t).
190 : * It doesn't check for overflow/underflow in tm->tm_year output. Other than
191 : * that, it never overflows or underflows. It assumes that that time_t is
192 : * signed.
193 : *
194 : * This implements the inverse of the POSIX formula
195 : * (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15)
196 : * for all time_t values, no matter the size, as long as tm->tm_year doesn't
197 : * overflow or underflow. The formula is: tm_sec + tm_min*60 + tm_hour*3600
198 : * + tm_yday*86400 + (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
199 : * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400.
200 : *
201 : * License : GNU General Public License v2.0 from
202 : * https://github.com/pts/minilibc686/
203 : * @param time_t
204 : * @return tm
205 : */
206 :
207 : static inline struct tm*
208 1751132 : mini_gmtime_r(const time_t* timep, struct tm* tm) {
209 1751132 : const time_t ts = *timep;
210 1751132 : time_t t = ts / 86400;
211 1751132 : unsigned hms =
212 1751132 : ts %
213 : 86400; /* -86399 <= hms <= 86399. This needs sizeof(int) >= 4. */
214 1751132 : time_t c, f;
215 1751132 : unsigned yday; /* 0 <= yday <= 426. Also fits to an `unsigned short', but
216 : `int' is faster. */
217 1751132 : unsigned a; /* 0 <= a <= 2133. Also fits to an `unsigned short', but `int'
218 : is faster. */
219 1751132 : if((int) hms < 0) {
220 0 : --t;
221 0 : hms += 86400;
222 : } /* Fix quotient and negative remainder if ts was negative (i.e. before
223 : year 1970 CE). */
224 : /* Now: -24856 <= t <= 24855. */
225 1751132 : tm->tm_sec = hms % 60;
226 1751132 : hms /= 60;
227 1751132 : tm->tm_min = hms % 60;
228 1751132 : tm->tm_hour = hms / 60;
229 1751132 : if(sizeof(time_t) >
230 : 4) { /* Optimization. For int32_t, this would keep t intact, so we won't
231 : have to do it. This produces unreachable code. */
232 1751132 : f = (t + 4) % 7;
233 1751132 : if(f < 0)
234 0 : f += 7; /* Fix negative remainder if (t + 4) was negative. */
235 : /* Now 0 <= f <= 6. */
236 1751132 : tm->tm_wday = f;
237 1751132 : c = (t << 2) + 102032;
238 1751132 : f = c / 146097;
239 1751132 : if(c % 146097 < 0)
240 0 : --f; /* Fix negative remainder if c was negative. */
241 1751132 : --f;
242 1751132 : t += f;
243 1751132 : f >>= 2;
244 1751132 : t -= f;
245 1751132 : f = (t << 2) + 102035;
246 1751132 : c = f / 1461;
247 1751132 : if(f % 1461 < 0)
248 0 : --c; /* Fix negative remainder if f was negative. */
249 : } else {
250 : tm->tm_wday = (t + 24861) % 7; /* t + 24861 >= 0. */
251 : /* Now: -24856 <= t <= 24855. */
252 : c = ((t << 2) + 102035) / 1461;
253 : }
254 1751132 : yday = t - 365 * c - (c >> 2) + 25568;
255 : /* Now: 0 <= yday <= 425. */
256 1751132 : a = yday * 5 + 8;
257 : /* Now: 8 <= a <= 2133. */
258 1751132 : tm->tm_mon = a / 153;
259 1751132 : a %= 153; /* No need to fix if a < 0, because a cannot be negative here. */
260 : /* Now: 2 <= tm->tm_mon <= 13. */
261 : /* Now: 0 <= a <= 152. */
262 1751132 : tm->tm_mday = 1 + a / 5; /* No need to fix if a < 0, because a cannot be
263 : negative here. */
264 : /* Now: 1 <= tm->tm_mday <= 31. */
265 1751132 : if(tm->tm_mon >= 12) {
266 0 : tm->tm_mon -= 12;
267 : /* Now: 0 <= tm->tm_mon <= 1. */
268 0 : ++c;
269 0 : yday -= 366;
270 : } else { /* Check for leap year (in c). */
271 : /* Now: 2 <= tm->tm_mon <= 11. */
272 : /* 1903: not leap; 1904: leap, 1900: not leap; 2000: leap */
273 : /* With sizeof(time_t) == 4, we have 1901 <= year <= 2038; of these
274 : * years only 2000 is divisble by 100, and that's a leap year, no we
275 : * optimize the check to `(c & 3) == 0' only.
276 : */
277 1751132 : if(!((c & 3) == 0 &&
278 1751132 : (sizeof(time_t) <= 4 || c % 100 != 0 || (c + 300) % 400 == 0)))
279 0 : --yday; /* These `== 0' comparisons work even if c < 0. */
280 : }
281 1751132 : tm->tm_year =
282 : c; /* This assignment may overflow or underflow, we don't check it.
283 : Example: time_t is a huge int64_t, tm->tm_year is int32_t. */
284 : /* Now: 0 <= tm->tm_mon <= 11. */
285 : /* Now: 0 <= yday <= 365. */
286 1751132 : tm->tm_yday = yday;
287 1751132 : tm->tm_isdst = 0;
288 1751132 : return tm;
289 : }
290 :
291 : static inline struct tm*
292 1751211 : mini_gmtime(const time_t* timep) {
293 1751211 : static struct tm tm;
294 1751211 : return mini_gmtime_r(timep, &tm);
295 : }
296 :
297 : static inline ssize_t
298 1751211 : format_timeval(struct timeval* tv, char* buf, size_t sz) {
299 1751211 : ssize_t written = -1;
300 1751211 : struct tm* gm = mini_gmtime(&tv->tv_sec);
301 :
302 :
303 1751057 : written = (ssize_t) strftime(buf, sz, "%Y-%m-%d %H:%M:%S", gm);
304 1751057 : if((written > 0) && ((size_t) written < sz)) {
305 1751230 : int w = snprintf(buf + written, sz - (size_t) written, ".%06ld",
306 : tv->tv_usec);
307 1751230 : written = (w > 0) ? written + w : -1;
308 : }
309 :
310 1751057 : return written;
311 : }
312 :
313 : /**
314 : * format_timestamp_to - safely format a timestamp for logging messages
315 : *
316 : * This function produes a timestamp that can be used to prefix logging
317 : * messages. Since we are actively intercepting system calls, the formatting
318 : * MUST NOT rely on internal system calls, otherwise we risk recursively
319 : * calling ourselves for each syscall generated. Also, we cannot rely on
320 : * the C formatting functions asctime, ctime, gmtime, localtime, mktime,
321 : * asctime_r, ctime_r, gmtime_r, localtime_r, since they acquire a
322 : * non-reentrant lock to determine the caller's timezone (yes, the assumedly
323 : * reentrant *_r versions of the functions exhibit this problem as well,
324 : * see https://sourceware.org/bugzilla/show_bug.cgi?id=16145). To solve this
325 : * issue and still get readable timestamps, we determine and cache the
326 : * timezone when the logger is created so that the lock is only held once, by
327 : * one thread exactly, and we pass it as an argument whenever we need to
328 : * format a timestamp. If no timezone is provided, we just format the epoch.
329 : *
330 : */
331 : template <typename Buffer>
332 : static inline void
333 1751080 : format_timestamp_to(Buffer&& buffer) {
334 :
335 : struct ::timeval tv;
336 :
337 1751080 : int rv = ::syscall_no_intercept(SYS_gettimeofday, &tv, NULL);
338 :
339 1751241 : if(::syscall_error_code(rv) != 0) {
340 0 : return;
341 : }
342 :
343 : char buf[28];
344 :
345 1751241 : if(format_timeval(&tv, buf, sizeof(buf)) > 0) {
346 1751231 : fmt::format_to(std::back_inserter(buffer), "[{}] ", buf);
347 : }
348 : }
349 :
350 : template <typename Buffer>
351 : static inline void
352 1271135 : format_syscall_info_to(Buffer&& buffer, gkfs::syscall::info info) {
353 :
354 1271135 : const auto ttid = syscall_no_intercept(SYS_gettid);
355 1271220 : fmt::format_to(std::back_inserter(buffer), "[{}] [syscall] ", ttid);
356 :
357 : char o;
358 : char t;
359 :
360 1271160 : switch(gkfs::syscall::origin(info)) {
361 : case gkfs::syscall::from_internal_code:
362 : o = 'i';
363 : break;
364 898062 : case gkfs::syscall::from_external_code:
365 898062 : o = 'a';
366 898062 : break;
367 1097 : default:
368 1097 : o = '?';
369 1097 : break;
370 : }
371 :
372 1271160 : switch(gkfs::syscall::target(info)) {
373 : case gkfs::syscall::to_hook:
374 : t = 'h';
375 : break;
376 205295 : case gkfs::syscall::to_kernel:
377 205295 : t = 'k';
378 205295 : break;
379 1097 : default:
380 1097 : t = '?';
381 1097 : break;
382 : }
383 :
384 1271160 : const std::array<char, 5> tmp = {'[', o, t, ']', ' '};
385 1271160 : fmt::format_to(std::back_inserter(buffer),
386 1271160 : fmt::string_view(tmp.data(), tmp.size()));
387 1271133 : }
388 :
389 : } // namespace detail
390 :
391 : enum { max_buffer_size = LIBGKFS_LOG_MESSAGE_SIZE };
392 :
393 : using static_buffer = fmt::basic_memory_buffer<char, max_buffer_size>;
394 :
395 : struct logger {
396 :
397 : logger(const std::string& opts, const std::string& path,
398 : bool log_per_process, bool trunc
399 : #ifdef GKFS_DEBUG_BUILD
400 : ,
401 : const std::string& filter, int verbosity
402 : #endif
403 : );
404 :
405 : ~logger();
406 :
407 : template <typename... Args>
408 : inline void
409 480091 : log(log_level level, const char* const func, const int lineno,
410 : Args&&... args) {
411 :
412 480091 : if(!(level & log_mask_)) {
413 32 : return;
414 : }
415 :
416 960118 : static_buffer buffer;
417 480059 : detail::format_timestamp_to(buffer);
418 480058 : fmt::format_to(std::back_inserter(buffer), "[{}] [{}] ",
419 480058 : log_process_id_, lookup_level_name(level));
420 :
421 480058 : if(!!(level & log::debug)) {
422 457546 : fmt::format_to(std::back_inserter(buffer), "<{}():{}> ", func,
423 : lineno);
424 : }
425 :
426 480602 : fmt::format_to(std::back_inserter(buffer), std::forward<Args>(args)...);
427 480058 : fmt::format_to(std::back_inserter(buffer), "\n");
428 480058 : detail::log_buffer(log_fd_, buffer);
429 : }
430 :
431 : inline int
432 : log(log_level level, const char* fmt, va_list ap) {
433 :
434 : if(!(level & log_mask_)) {
435 : return 0;
436 : }
437 :
438 : // we use buffer views to compose the logging messages to
439 : // avoid copying buffers as much as possible
440 : struct buffer_view {
441 : const void* addr;
442 : std::size_t size;
443 : };
444 :
445 : // helper lambda to print an iterable of buffer_views
446 : const auto log_buffer_views = [this](const auto& buffers) {
447 : std::size_t n = 0;
448 :
449 : for(const auto& bv : buffers) {
450 : if(bv.addr != nullptr) {
451 : detail::log_buffer(log_fd_, bv.addr, bv.size);
452 : n += bv.size;
453 : }
454 : }
455 :
456 : return n;
457 : };
458 :
459 :
460 : static_buffer prefix;
461 : detail::format_timestamp_to(prefix);
462 : fmt::format_to(std::back_inserter(prefix), "[{}] [{}] ",
463 : log_process_id_, lookup_level_name(level));
464 :
465 : char buffer[max_buffer_size];
466 : const int n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
467 :
468 : std::array<buffer_view, 3> buffers{};
469 :
470 : int i = 0;
471 : int m = 0;
472 : const char* addr = buffer;
473 : const char* p = nullptr;
474 : while((p = std::strstr(addr, "\n")) != nullptr) {
475 : buffers[0] = buffer_view{prefix.data(), prefix.size()};
476 : buffers[1] =
477 : buffer_view{addr, static_cast<std::size_t>(p - addr) + 1};
478 :
479 : m += log_buffer_views(buffers);
480 : addr = p + 1;
481 : ++i;
482 : }
483 :
484 : // original line might not end with (or include) '\n'
485 : if(buffer[n - 1] != '\n') {
486 : buffers[0] = buffer_view{prefix.data(), prefix.size()};
487 : buffers[1] = buffer_view{
488 : addr, static_cast<std::size_t>(&buffer[n] - addr)};
489 : buffers[2] = buffer_view{"\n", 1};
490 :
491 : m += log_buffer_views(buffers);
492 : }
493 :
494 : return m;
495 : }
496 :
497 : template <typename... Args>
498 : static inline void
499 0 : log_message(std::FILE* fp, Args&&... args) {
500 0 : log_message(::fileno(fp), std::forward<Args>(args)...);
501 0 : }
502 :
503 : template <typename... Args>
504 : static inline void
505 0 : log_message(int fd, Args&&... args) {
506 :
507 0 : if(fd < 0) {
508 0 : throw std::runtime_error("Invalid file descriptor");
509 : }
510 :
511 0 : static_buffer buffer;
512 0 : fmt::format_to(std::back_inserter(buffer), std::forward<Args>(args)...);
513 0 : fmt::format_to(std::back_inserter(buffer), "\n");
514 0 : detail::log_buffer(fd, buffer);
515 0 : }
516 :
517 : void
518 : log_syscall(syscall::info info, const long syscall_number,
519 : const long args[6], std::optional<long> result = {});
520 :
521 : static std::shared_ptr<logger>&
522 59097371 : global_logger() {
523 59097371 : static std::shared_ptr<logger> s_global_logger;
524 59097371 : return s_global_logger;
525 : }
526 :
527 : int log_fd_;
528 : int log_process_id_;
529 : log_level log_mask_;
530 :
531 : #ifdef GKFS_DEBUG_BUILD
532 : std::bitset<512> filtered_syscalls_;
533 : int debug_verbosity_;
534 : #endif
535 : };
536 :
537 : // the following static functions can be used to interact
538 : // with a globally registered logger instance
539 :
540 : template <typename... Args>
541 : static inline void
542 269 : create_global_logger(Args&&... args) {
543 :
544 269 : auto foo = std::make_shared<logger>(std::forward<Args>(args)...);
545 269 : logger::global_logger() = foo;
546 269 : }
547 :
548 : static inline void
549 : register_global_logger(logger&& lg) {
550 : logger::global_logger() = std::make_shared<logger>(std::move(lg));
551 : }
552 :
553 : static inline std::shared_ptr<logger>&
554 59115651 : get_global_logger() {
555 59115651 : return logger::global_logger();
556 : }
557 :
558 : static inline void
559 : destroy_global_logger() {
560 : logger::global_logger().reset();
561 : }
562 :
563 : } // namespace gkfs::log
564 :
565 : #define LOG(XXX, ...) LOG_##XXX(__VA_ARGS__)
566 :
567 : #ifndef GKFS_ENABLE_LOGGING
568 :
569 : #define LOG_INFO(...) \
570 : do { \
571 : } while(0);
572 : #define LOG_WARNING(...) \
573 : do { \
574 : } while(0);
575 : #define LOG_ERROR(...) \
576 : do { \
577 : } while(0);
578 : #define LOG_CRITICAL(...) \
579 : do { \
580 : } while(0);
581 : #define LOG_HERMES(...) \
582 : do { \
583 : } while(0);
584 : #define LOG_MERCURY(...) \
585 : do { \
586 : } while(0);
587 : #define LOG_SYSCALL(...) \
588 : do { \
589 : } while(0);
590 : #define LOG_DEBUG(...) \
591 : do { \
592 : } while(0);
593 : #define LOG_TRACE_READS(...) \
594 : do { \
595 : } while(0);
596 :
597 : #else // !GKFS_ENABLE_LOGGING
598 :
599 : #define LOG_INFO(...) \
600 : do { \
601 : if(gkfs::log::get_global_logger()) { \
602 : gkfs::log::get_global_logger()->log(gkfs::log::info, __func__, \
603 : __LINE__, __VA_ARGS__); \
604 : } \
605 : } while(0);
606 :
607 : #define LOG_WARNING(...) \
608 : do { \
609 : if(gkfs::log::get_global_logger()) { \
610 : gkfs::log::get_global_logger()->log(gkfs::log::warning, __func__, \
611 : __LINE__, __VA_ARGS__); \
612 : } \
613 : } while(0);
614 :
615 : #define LOG_ERROR(...) \
616 : do { \
617 : if(gkfs::log::get_global_logger()) { \
618 : gkfs::log::get_global_logger()->log(gkfs::log::error, __func__, \
619 : __LINE__, __VA_ARGS__); \
620 : } \
621 : } while(0);
622 :
623 : #define LOG_CRITICAL(...) \
624 : do { \
625 : if(gkfs::log::get_global_logger()) { \
626 : gkfs::log::get_global_logger()->log(gkfs::log::critical, __func__, \
627 : __LINE__, __VA_ARGS__); \
628 : } \
629 : } while(0);
630 :
631 : #define LOG_HERMES(...) \
632 : do { \
633 : if(gkfs::log::get_global_logger()) { \
634 : gkfs::log::get_global_logger()->log(gkfs::log::hermes, __func__, \
635 : __LINE__, __VA_ARGS__); \
636 : } \
637 : } while(0);
638 :
639 : #define LOG_MERCURY(...) \
640 : do { \
641 : if(gkfs::log::get_global_logger()) { \
642 : gkfs::log::get_global_logger()->log(gkfs::log::mercury, __func__, \
643 : __LINE__, __VA_ARGS__); \
644 : } \
645 : } while(0);
646 :
647 : #define LOG_TRACE_READS(...) \
648 : do { \
649 : if(gkfs::log::get_global_logger()) { \
650 : gkfs::log::get_global_logger()->log( \
651 : gkfs::log::trace_reads, __func__, __LINE__, __VA_ARGS__); \
652 : } \
653 : } while(0);
654 :
655 : #ifdef GKFS_DEBUG_BUILD
656 :
657 : #define LOG_SYSCALL(...) \
658 : do { \
659 : if(gkfs::log::get_global_logger()) { \
660 : gkfs::log::get_global_logger()->log_syscall(__VA_ARGS__); \
661 : } \
662 : } while(0);
663 :
664 : #define LOG_DEBUG(...) \
665 : do { \
666 : if(gkfs::log::get_global_logger()) { \
667 : gkfs::log::get_global_logger()->log(gkfs::log::debug, __func__, \
668 : __LINE__, __VA_ARGS__); \
669 : } \
670 : } while(0);
671 :
672 : #else // ! GKFS_DEBUG_BUILD
673 :
674 : #define LOG_SYSCALL(...) \
675 : do { \
676 : } while(0);
677 : #define LOG_DEBUG(...) \
678 : do { \
679 : } while(0);
680 :
681 : #endif // ! GKFS_DEBUG_BUILD
682 : #endif // !GKFS_ENABLE_LOGGING
683 :
684 : #endif // LIBGKFS_LOGGING_HPP
|