From 3222de6c9ce0ac697ccf0c0984d2519df4fc8fef Mon Sep 17 00:00:00 2001 From: trapexit Date: Thu, 12 Feb 2026 23:50:38 -0500 Subject: [PATCH] Fixup debug output (#1617) --- libfuse/{lib => include}/debug.hpp | 41 +- libfuse/include/fuse.h | 3 - libfuse/include/fuse_cfg.hpp | 15 +- libfuse/lib/debug.cpp | 1687 +++++++++++------ libfuse/lib/fuse.cpp | 19 +- libfuse/lib/fuse_cfg.cpp | 25 + libfuse/lib/fuse_lowlevel.cpp | 74 + mkdocs/docs/support.md | 39 +- src/config.cpp | 6 +- src/config.hpp | 8 +- src/config_debug.cpp | 63 + src/config_debug.hpp | 31 + ...ig_log_metrics.cpp => config_log_file.cpp} | 35 +- ...ig_log_metrics.hpp => config_log_file.hpp} | 4 +- src/fuse_init.cpp | 8 + src/mergerfs.cpp | 6 +- src/mergerfs_collect_info.cpp | 7 + 17 files changed, 1446 insertions(+), 625 deletions(-) rename libfuse/{lib => include}/debug.hpp (59%) create mode 100644 src/config_debug.cpp create mode 100644 src/config_debug.hpp rename src/{config_log_metrics.cpp => config_log_file.cpp} (65%) rename src/{config_log_metrics.hpp => config_log_file.hpp} (93%) diff --git a/libfuse/lib/debug.hpp b/libfuse/include/debug.hpp similarity index 59% rename from libfuse/lib/debug.hpp rename to libfuse/include/debug.hpp index f1ce235e..26348935 100644 --- a/libfuse/lib/debug.hpp +++ b/libfuse/include/debug.hpp @@ -18,39 +18,52 @@ #pragma once +#include "fuse_kernel.h" + +#include #include -#include "fuse_kernel.h" -void debug_fuse_open_out(const uint64_t unique, +int fuse_debug_set_output(const std::string &filepath); + +void fuse_debug_open_out(const uint64_t unique, const struct fuse_open_out *arg, const uint64_t argsize); -void debug_fuse_init_in(const struct fuse_init_in *arg); -void debug_fuse_init_out(const uint64_t unique, +void fuse_debug_init_in(const struct fuse_init_in *arg); +void fuse_debug_init_out(const uint64_t unique, const struct fuse_init_out *arg, const uint64_t argsize); -void debug_fuse_entry_out(const uint64_t unique, +void fuse_debug_entry_out(const uint64_t unique, const struct fuse_entry_out *arg, const uint64_t argsize); -void debug_fuse_attr_out(const uint64_t unique, +void fuse_debug_attr_out(const uint64_t unique, const struct fuse_attr_out *arg, const uint64_t argsize); -void debug_fuse_entry_open_out(const uint64_t unique, +void fuse_debug_entry_open_out(const uint64_t unique, const struct fuse_entry_out *earg, const struct fuse_open_out *oarg); -void debug_fuse_readlink(const uint64_t unique, +void fuse_debug_readlink(const uint64_t unique, const char *linkname); -void debug_fuse_write_out(const uint64_t unique, +void fuse_debug_write_out(const uint64_t unique, const struct fuse_write_out *arg); -void debug_fuse_statfs_out(const uint64_t unique, +void fuse_debug_statfs_out(const uint64_t unique, const struct fuse_statfs_out *arg); -void debug_fuse_getxattr_out(const uint64_t unique, +void fuse_debug_getxattr_out(const uint64_t unique, const struct fuse_getxattr_out *arg); -void debug_fuse_lk_out(const uint64_t unique, +void fuse_debug_lk_out(const uint64_t unique, const struct fuse_lk_out *arg); -void debug_fuse_bmap_out(const uint64_t unique, +void fuse_debug_bmap_out(const uint64_t unique, const struct fuse_bmap_out *arg); -void debug_fuse_in_header(const struct fuse_in_header *hdr); +void fuse_debug_statx_out(const uint64_t unique, + const struct fuse_statx_out *arg); +void fuse_debug_data_out(const uint64_t unique, + const size_t size); +void fuse_debug_ioctl_out(const uint64_t unique, + const struct fuse_ioctl_out *arg); +void fuse_debug_poll_out(const uint64_t unique, + const struct fuse_poll_out *arg); +void fuse_debug_in_header(const struct fuse_in_header *hdr); +void fuse_debug_out_header(const struct fuse_out_header *hdr); std::string fuse_debug_init_flag_name(const uint64_t); diff --git a/libfuse/include/fuse.h b/libfuse/include/fuse.h index 909227b0..4d2bbacb 100644 --- a/libfuse/include/fuse.h +++ b/libfuse/include/fuse.h @@ -359,9 +359,6 @@ int fuse_main(int argc, void fuse_populate_maintenance_thread(struct fuse *fuse); -int fuse_log_metrics_get(void); -void fuse_log_metrics_set(int enabled); - /** * Iterate over cache removing stale entries diff --git a/libfuse/include/fuse_cfg.hpp b/libfuse/include/fuse_cfg.hpp index 20f77bc6..87dce1a7 100644 --- a/libfuse/include/fuse_cfg.hpp +++ b/libfuse/include/fuse_cfg.hpp @@ -3,6 +3,8 @@ #include "base_types.h" #include +#include +#include #include #define FUSE_CFG_INVALID_ID -1 @@ -11,6 +13,9 @@ struct fuse_cfg_t { + template + using sp = std::shared_ptr; + s64 uid = FUSE_CFG_INVALID_ID; bool valid_uid() const; @@ -23,7 +28,15 @@ struct fuse_cfg_t s64 remember_nodes = 0; bool debug = false; - + std::shared_ptr log_file() const; + void log_file(std::shared_ptr); + std::shared_ptr log_filepath() const; + void log_filepath(const std::string &); +private: + std::shared_ptr _log_file = sp(stderr,[](FILE*){}); + std::shared_ptr _log_filepath = sp(); + +public: int max_background = 0; int congestion_threshold = 0; u32 max_pages = 32; diff --git a/libfuse/lib/debug.cpp b/libfuse/lib/debug.cpp index b82bce98..59e39617 100644 --- a/libfuse/lib/debug.cpp +++ b/libfuse/lib/debug.cpp @@ -1,7 +1,7 @@ /* ISC License - Copyright (c) 2021, Antonio SJ Musumeci + Copyright (c) 2026, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -22,69 +22,133 @@ #include "syslog.hpp" +#include "fuse_cfg.hpp" #include "fuse_kernel.h" #include "fmt/core.h" +#include "fmt/format.h" +#include #include #include #include -#include #include #include - -static FILE *g_OUTPUT = NULL; +#include #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) +static +void +_fclose_deleter(FILE *f_) +{ + if(f_ == nullptr) + return; + if(f_ == stdin) + return; + if(f_ == stdout) + return; + if(f_ == stderr) + return; + + fclose(f_); +} + static int -debug_set_output_null() +_debug_set_output_stderr() { - g_OUTPUT = stderr; - setvbuf(g_OUTPUT,NULL,_IOLBF,0); + auto new_log_file = std::shared_ptr(stderr,[](FILE*){}); + + fuse_cfg.log_file(new_log_file); + fuse_cfg.log_filepath("/dev/stderr"); return 0; } static int -debug_set_output_filepath(const char *filepath_) +_debug_set_output_filepath(const std::string &filepath_) { - FILE *tmp; + FILE *tmp_new_file; - tmp = fopen(filepath_,"a"); - if(tmp == NULL) + tmp_new_file = fopen(filepath_.c_str(),"a"); + if(tmp_new_file == NULL) return -errno; - g_OUTPUT = tmp; - setvbuf(g_OUTPUT,NULL,_IOLBF,0); + setvbuf(tmp_new_file,NULL,_IOLBF,0); + + auto new_log_file = + std::shared_ptr(tmp_new_file,::_fclose_deleter); + + fuse_cfg.log_file(new_log_file); + fuse_cfg.log_filepath(filepath_); return 0; } int -debug_set_output(const char *filepath_) +fuse_debug_set_output(const std::string &filepath_) { - if(filepath_ == NULL) - return debug_set_output_null(); + if(filepath_.empty()) + return _debug_set_output_stderr(); - return debug_set_output_filepath(filepath_); + return _debug_set_output_filepath(filepath_.c_str()); } static -__attribute__((constructor)) -void -debug_constructor(void) +uint64_t +_timestamp_ns(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC,&ts); + + return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec; +} + +static +std::string +_quoted(const char *str_) +{ + std::string out; + + out += '"'; + for(const char *p = str_; *p; p++) + { + if(*p == '"' || *p == '\\') + out += '\\'; + out += *p; + } + out += '"'; + + return out; +} + +static +std::string +_quoted(const char *data_, + size_t len_) { - debug_set_output(NULL); + std::string out; + + out += '"'; + for(size_t i = 0; i < len_; i++) + { + if(data_[i] == '"' || data_[i] == '\\') + out += '\\'; + out += data_[i]; + } + out += '"'; + + return out; } static const char* -open_accmode_to_str(const int flags_) +_open_accmode_to_str(const int flags_) { switch(flags_ & O_ACCMODE) { @@ -104,7 +168,7 @@ open_accmode_to_str(const int flags_) static const char* -fuse_write_flag_to_str(const uint32_t offset_) +_fuse_write_flag_to_str(const uint32_t offset_) { switch(1 << offset_) { @@ -123,7 +187,7 @@ fuse_write_flag_to_str(const uint32_t offset_) static const char* -open_flag_to_str(const uint64_t offset_) +_open_flag_to_str(const uint64_t offset_) { switch(1 << offset_) { @@ -166,7 +230,7 @@ open_flag_to_str(const uint64_t offset_) static const char* -fuse_flag_to_str(const uint64_t offset_) +_fuse_flag_to_str(const uint64_t offset_) { switch(1ULL << offset_) { @@ -272,10 +336,12 @@ fuse_debug_init_flag_name(const uint64_t flag_) #undef FUSE_INIT_FLAG_CASE static -void -debug_open_flags(const uint32_t flags_) +std::string +_open_flags_str(const uint32_t flags_) { - fmt::print("{}, ",open_accmode_to_str(flags_)); + std::string buf; + + buf += _open_accmode_to_str(flags_); for(size_t i = 0; i < (sizeof(flags_) * 8); i++) { const char *str; @@ -283,12 +349,15 @@ debug_open_flags(const uint32_t flags_) if(!(flags_ & (1 << i))) continue; - str = open_flag_to_str(i); + str = _open_flag_to_str(i); if(str == NULL) continue; - fmt::print("{}, ",str); + buf += '|'; + buf += str; } + + return buf; } #define FOPEN_FLAG_CASE(X) case FOPEN_##X: return #X @@ -296,7 +365,7 @@ debug_open_flags(const uint32_t flags_) static const char* -fuse_fopen_flag_to_str(const uint32_t offset_) +_fuse_fopen_flag_to_str(const uint32_t offset_) { switch(1 << offset_) { @@ -312,82 +381,191 @@ fuse_fopen_flag_to_str(const uint32_t offset_) #undef FOPEN_FLAG_CASE -void -debug_fuse_open_out(const struct fuse_open_out *arg_) +static +std::string +_fopen_flags_str(const uint32_t flags_) { - fmt::print(stderr, - "fuse_open_out:" - " fh=0x{:#08x};" - " open_flags=0x{:#04x} (", - arg_->fh, - arg_->open_flags); - for(size_t i = 0; i < (sizeof(arg_->open_flags) * 8); i++) + std::string buf; + + for(size_t i = 0; i < (sizeof(flags_) * 8); i++) + { + const char *str; + + if(!(flags_ & (1 << i))) + continue; + + str = _fuse_fopen_flag_to_str(i); + if(str == NULL) + continue; + + if(!buf.empty()) + buf += '|'; + buf += str; + } + + return buf; +} + +static +std::string +_write_flags_str(const uint32_t flags_) +{ + std::string buf; + + for(size_t i = 0; i < (sizeof(flags_) * 8); i++) + { + const char *str; + + if(!(flags_ & (1 << i))) + continue; + + str = _fuse_write_flag_to_str(i); + if(str == NULL) + continue; + + if(!buf.empty()) + buf += '|'; + buf += str; + } + + return buf; +} + +static +std::string +_fuse_init_flags_str(const uint64_t flags_) +{ + std::string buf; + + for(uint64_t i = 0; i < (sizeof(flags_)*8); i++) { const char *str; - if(!(arg_->open_flags & (1 << i))) + if(!(flags_ & (1ULL << i))) continue; - str = fuse_fopen_flag_to_str(i); + str = _fuse_flag_to_str(i); if(str == NULL) continue; - fmt::print(stderr,"{},",str); + if(!buf.empty()) + buf += '|'; + buf += str; + } + + return buf; +} + +static +std::string +_xattr_value_to_printable(const char *data_, + uint32_t size_) +{ + bool is_printable = true; + + for(uint32_t i = 0; i < size_; i++) + { + unsigned char c = (unsigned char)data_[i]; + if(c < 0x20 || c > 0x7E) + { + is_printable = false; + break; + } } - fmt::print(stderr,");\n"); + + if(is_printable) + return _quoted(data_,size_); + + std::string hex; + hex += '"'; + for(uint32_t i = 0; i < size_; i++) + { + if(i > 0) + hex += ' '; + hex += fmt::format("{:02X}",(unsigned char)data_[i]); + } + hex += '"'; + + return hex; +} + +void +fuse_debug_open_out(const uint64_t unique_, + const struct fuse_open_out *arg_, + const uint64_t argsize_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " fh=0x{:08X}" + " open_flags=0x{:04X}" + " open_flags_str=\"{}\"" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + argsize_, + arg_->fh, + arg_->open_flags, + _fopen_flags_str(arg_->open_flags)); } static void -debug_fuse_lookup(const void *arg_) +_debug_fuse_lookup(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name = (const char*)arg_; - fmt::print(g_OUTPUT, - "fuse_lookup:" - " name={};" - "\n" - , - name); + fmt::print(output.get(), + "name={}\n", + :: _quoted(name)); } static void -debug_fuse_getattr_in(const void *arg_) +_debug_fuse_getattr_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_getattr_in *arg = (const fuse_getattr_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_getattr_in:" - " getattr_flags=0x{:#08x};" - " fh=0x{:08x};\n", + fmt::print(output.get(), + "getattr_flags=0x{:08X}" + " fh=0x{:08X}" + "\n", arg->getattr_flags, arg->fh); } static void -debug_fuse_setattr_in(const void *arg_) +_debug_fuse_setattr_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_setattr_in *arg = (const fuse_setattr_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_setattr_in:" - " valid={};" - " fh=0x{:#08x};" - " size={};" - " lock_owner={};" - " atime={};" - " atimensec={};" - " mtime={};" - " mtimensec={};" - " ctime={};" - " ctimensec={};" - " mode={:o};" - " uid={};" - " gid={};" - "\n" - , + fmt::print(output.get(), + "valid={}" + " fh=0x{:08X}" + " size={}" + " lock_owner={}" + " atime={}" + " atimensec={}" + " mtime={}" + " mtimensec={}" + " ctime={}" + " ctimensec={}" + " mode={:o}" + " uid={}" + " gid={}" + "\n", arg->valid, arg->fh, arg->size, @@ -405,106 +583,110 @@ debug_fuse_setattr_in(const void *arg_) static void -debug_fuse_access_in(const void *arg_) +_debug_fuse_access_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_access_in *arg = (const fuse_access_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_access_in:" - " mask=0x{:#08x};" - "\n" - , + fmt::print(output.get(), + "mask=0x{:08X}" + "\n", arg->mask); } static void -debug_fuse_mknod_in(const void *arg_) +_debug_fuse_mknod_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_mknod_in *arg = (const fuse_mknod_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_mknod_in:" - " mode={:o};" - " rdev=0x{#:08x};" - " umask={:o};" - "\n" - , + fmt::print(output.get(), + "mode={:o}" + " rdev=0x{:08X}" + " umask={:o}" + " name={}" + "\n", arg->mode, arg->rdev, - arg->umask); + arg->umask, + _quoted(PARAM(arg))); } static void -debug_fuse_mkdir_in(const void *arg_) +_debug_fuse_mkdir_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_mkdir_in *arg = (const fuse_mkdir_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_mkdir_in:" - " mode={:o};" - " umask={:o};" - " name=%s;" - "\n" - , + fmt::print(output.get(), + "mode={:o}" + " umask={:o}" + " name={}" + "\n", arg->mode, arg->umask, - PARAM(arg)); + _quoted(PARAM(arg))); } static void -debug_fuse_unlink(const void *arg_) +_debug_fuse_unlink(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name = (const char*)arg_; - fmt::print(g_OUTPUT, - "fuse_unlink:" - " name=%s;" - "\n" - , - name); + fmt::print(output.get(), + "name={}" + "\n", + :: _quoted(name)); } static void -debug_fuse_rmdir(const void *arg_) +_debug_fuse_rmdir(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name = (const char*)arg_; - fmt::print(g_OUTPUT, - "fuse_mkdir:" - " name=%s;" - "\n" - , - name); + fmt::print(output.get(), + "name={}" + "\n", + :: _quoted(name)); } static void -debug_fuse_symlink(const void *arg_) +_debug_fuse_symlink(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name; const char *linkname; name = (const char*)arg_; linkname = (name + (strlen(name) + 1)); - fmt::print(g_OUTPUT, - "fuse_mkdir:" - " linkname=%s;" - " name=%s;" - "\n" - , - linkname, - name); + fmt::print(output.get(), + "name={}" + " linkname={}" + "\n", + _quoted(name), + _quoted(linkname)); } static void -debug_fuse_rename_in(const void *arg_) +_debug_fuse_rename_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *oldname; const char *newname; const struct fuse_rename_in *arg = (const fuse_rename_in*)arg_; @@ -512,195 +694,190 @@ debug_fuse_rename_in(const void *arg_) oldname = PARAM(arg); newname = (oldname + strlen(oldname) + 1); - fmt::print(g_OUTPUT, - "fuse_rename_in:" - " oldname=%s;" - " newdir={};" - " newname=%s;" - "\n" - , - oldname, + fmt::print(output.get(), + "oldname={}" + " newdir={}" + " newname={}" + "\n", + _quoted(oldname), arg->newdir, - newname); + _quoted(newname)); } static void -debug_fuse_link_in(const void *arg_) +_debug_fuse_link_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name; const struct fuse_link_in *arg = (const fuse_link_in*)arg_; name = PARAM(arg); - fmt::print(g_OUTPUT, - "fuse_link_in:" - " oldnodeid={};" - " name=%s;" - "\n" - , + fmt::print(output.get(), + "oldnodeid={}" + " name={}" + "\n", arg->oldnodeid, - name); + :: _quoted(name)); } static void -debug_fuse_create_in(const void *arg_) +_debug_fuse_create_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name; const struct fuse_create_in *arg = (const fuse_create_in*)arg_; name = PARAM(arg); - fmt::print(g_OUTPUT, - "fuse_create_in:" - " mode={:o};" - " umask={:o};" - " name=%s;" - " flags=0x%X (", + fmt::print(output.get(), + "mode={:o}" + " umask={:o}" + " name={}" + " flags=0x{:X}" + " flags_str=\"{}\"" + "\n", arg->mode, arg->umask, - name, - arg->flags); - debug_open_flags(arg->flags); - fmt::print(g_OUTPUT,");\n"); + _quoted(name), + arg->flags, + _open_flags_str(arg->flags)); } static void -debug_fuse_open_in(const void *arg_) +_debug_fuse_open_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_open_in *arg = (const fuse_open_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_open_in:" - " flags=0x%08X (", - arg->flags); - debug_open_flags(arg->flags); - fmt::print(g_OUTPUT,");\n"); + fmt::print(output.get(), + "flags=0x{:08X}" + " flags_str=\"{}\"" + "\n", + arg->flags, + _open_flags_str(arg->flags)); } static void -debug_fuse_read_in(const void *arg_) +_debug_fuse_read_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_read_in *arg = (const fuse_read_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_read_in:" - " fh=0x{:#08x};" - " offset={};" - " size={};" - " read_flags=%X;" - " lock_owner=0x{:#08x};" - " flags=0x%X (" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " offset={}" + " size={}" + " read_flags=0x{:X}" + " lock_owner=0x{:08X}" + " flags=0x{:X}" + " flags_str=\"{}\"" + "\n", arg->fh, arg->offset, arg->size, arg->read_flags, arg->lock_owner, - arg->flags); - debug_open_flags(arg->flags); - fmt::print(g_OUTPUT,");\n"); + arg->flags, + _open_flags_str(arg->flags)); } static void -debug_fuse_write_in(const void *arg_) +_debug_fuse_write_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_write_in *arg = (const fuse_write_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_write_in:" - " fh=0x{:#08x};" - " offset={};" - " size={};" - " lock_owner=0x{:#08x};" - " flags=0x%X (" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " offset={}" + " size={}" + " lock_owner=0x{:08X}" + " flags=0x{:X}" + " flags_str=\"{}\"" + " write_flags=0x{:X}" + " write_flags_str=\"{}\"" + "\n", arg->fh, arg->offset, arg->size, arg->lock_owner, - arg->flags); - debug_open_flags(arg->flags); - fmt::print(g_OUTPUT, - "); write_flags=0x%X (", - arg->write_flags); - for(size_t i = 0; i < (sizeof(arg->write_flags) * 8); i++) - { - const char *str; - - if(!(arg->write_flags & (1 << i))) - continue; - - str = fuse_write_flag_to_str(i); - if(str == NULL) - continue; - - fmt::print(g_OUTPUT,"%s,",str); - } - fmt::print(g_OUTPUT,");\n"); + arg->flags, + _open_flags_str(arg->flags), + arg->write_flags, + _write_flags_str(arg->write_flags)); } static void -debug_fuse_flush_in(const void *arg_) +_debug_fuse_flush_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_flush_in *arg = (const fuse_flush_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_flush_in:" - " fh=0x{:#08x};" - " lock_owner=0x{:#08x};" - "\n" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " lock_owner=0x{:08X}" + "\n", arg->fh, arg->lock_owner); } static void -debug_fuse_release_in(const void *arg_) +_debug_fuse_release_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_release_in *arg = (const fuse_release_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_release_in:" - " fh=0x{:#08x};" - " release_flags=0x%X;" - " lock_owner=0x{:#08x};" - " flags=0x%X (" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " release_flags=0x{:X}" + " lock_owner=0x{:08X}" + " flags=0x{:X}" + " flags_str=\"{}\"" + "\n", arg->fh, arg->release_flags, arg->lock_owner, - arg->flags); - debug_open_flags(arg->flags); - fmt::print(g_OUTPUT,");\n"); + arg->flags, + _open_flags_str(arg->flags)); } static void -debug_fuse_fsync_in(const void *arg_) +_debug_fuse_fsync_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_fsync_in *arg = (const fuse_fsync_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_fsync_in:" - " fh=0x{:#08x};" - " fsync_flags=0x%X;" - "\n" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " fsync_flags=0x{:X}" + "\n", arg->fh, arg->fsync_flags); } static void -debug_fuse_setxattr_in(const void *arg_) +_debug_fuse_setxattr_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name; const char *value; const struct fuse_setxattr_in *arg = (const fuse_setxattr_in*)arg_; @@ -708,117 +885,343 @@ debug_fuse_setxattr_in(const void *arg_) name = PARAM(arg); value = (name + strlen(name) + 1); - fmt::print(g_OUTPUT, - "fuse_setxattr_in:" - " size={};" - " flags=0x%X;" - " name=%s;" - " value=%s;" - "\n" - , + fmt::print(output.get(), + "size={}" + " flags=0x{:X}" + " name={}" + " value={}" + "\n", arg->size, arg->flags, - name, - value); + _quoted(name), + _xattr_value_to_printable(value,arg->size)); } static void -debug_fuse_getxattr_in(const void *arg_) +_debug_fuse_getxattr_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name; const struct fuse_getxattr_in *arg = (const fuse_getxattr_in*)arg_; name = PARAM(arg); - fmt::print(g_OUTPUT, - "fuse_getxattr_in:" - " size={};" - " name=%s;" - "\n" - , + fmt::print(output.get(), + "size={}" + " name={}" + "\n", arg->size, - name); + :: _quoted(name)); } static void -debug_fuse_listxattr(const void *arg_) +_debug_fuse_listxattr(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_getxattr_in *arg = (const fuse_getxattr_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_listxattr:" - " size={};" - "\n" - , + fmt::print(output.get(), + "size={}" + "\n", arg->size); } static void -debug_fuse_removexattr(const void *arg_) +_debug_fuse_removexattr(const void *arg_) { + auto output = fuse_cfg.log_file(); + const char *name = (const char*)arg_; - fmt::print(g_OUTPUT, - "fuse_removexattr:" - " name=%s;" - "\n" - , - name); + fmt::print(output.get(), + "name={}" + "\n", + :: _quoted(name)); } static void -debug_fuse_fallocate_in(const void *arg_) +_debug_fuse_fallocate_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_fallocate_in *arg = (const fuse_fallocate_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_fallocate_in:" - " fh=0x{:#08x};" - " offset={};" - " length={};" - " mode={:o};" - "\n" - , + fmt::print(output.get(), + "fh=0x{:08X}" + " offset={}" + " length={}" + " mode={:o}" + "\n", arg->fh, arg->offset, arg->length, arg->mode); } +static +void +_debug_fuse_forget_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_forget_in *arg = (const fuse_forget_in*)arg_; + + fmt::print(output.get(), + "nlookup={}" + "\n", + arg->nlookup); +} + +static +void +_debug_fuse_batch_forget_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_batch_forget_in *arg = (const fuse_batch_forget_in*)arg_; + const struct fuse_forget_one *items = (const fuse_forget_one*)PARAM(arg); + + fmt::print(output.get(), + "count={}", + arg->count); + for(uint32_t i = 0; i < arg->count; i++) + { + fmt::print(output.get(), + " item.{}={{nodeid={},nlookup={}}}", + i, + items[i].nodeid, + items[i].nlookup); + } + fmt::print(output.get(),"\n"); +} + +static +void +_debug_fuse_lk_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_lk_in *arg = (const fuse_lk_in*)arg_; + + fmt::print(output.get(), + "fh=0x{:08X}" + " owner=0x{:016X}" + " lk_start={}" + " lk_end={}" + " lk_type={}" + " lk_pid={}" + " lk_flags=0x{:X}" + "\n", + arg->fh, + arg->owner, + arg->lk.start, + arg->lk.end, + arg->lk.type, + arg->lk.pid, + arg->lk_flags); +} + +static +void +_debug_fuse_bmap_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_bmap_in *arg = (const fuse_bmap_in*)arg_; + + fmt::print(output.get(), + "block={}" + " blocksize={}" + "\n", + arg->block, + arg->blocksize); +} + +static +void +_debug_fuse_ioctl_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_ioctl_in *arg = (const fuse_ioctl_in*)arg_; + + fmt::print(output.get(), + "fh=0x{:08X}" + " flags=0x{:X}" + " cmd=0x{:X}" + " arg=0x{:016X}" + " in_size={}" + " out_size={}" + "\n", + arg->fh, + arg->flags, + arg->cmd, + arg->arg, + arg->in_size, + arg->out_size); +} + +static +void +_debug_fuse_poll_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_poll_in *arg = (const fuse_poll_in*)arg_; + + fmt::print(output.get(), + "fh=0x{:08X}" + " kh=0x{:016X}" + " flags=0x{:X}" + " events=0x{:X}" + "\n", + arg->fh, + arg->kh, + arg->flags, + arg->events); +} + +static +void +_debug_fuse_rename2_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const char *oldname; + const char *newname; + const struct fuse_rename2_in *arg = (const fuse_rename2_in*)arg_; + + oldname = PARAM(arg); + newname = (oldname + strlen(oldname) + 1); + + fmt::print(output.get(), + "oldname={}" + " newdir={}" + " newname={}" + " flags=0x{:X}" + "\n", + _quoted(oldname), + arg->newdir, + _quoted(newname), + arg->flags); +} + +static +void +_debug_fuse_lseek_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_lseek_in *arg = (const fuse_lseek_in*)arg_; + + fmt::print(output.get(), + "fh=0x{:08X}" + " offset={}" + " whence={}" + "\n", + arg->fh, + arg->offset, + arg->whence); +} + +static +void +_debug_fuse_copy_file_range_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_copy_file_range_in *arg = (const fuse_copy_file_range_in*)arg_; + + fmt::print(output.get(), + "fh_in=0x{:08X}" + " off_in={}" + " nodeid_out={}" + " fh_out=0x{:08X}" + " off_out={}" + " len={}" + " flags=0x{:X}" + "\n", + arg->fh_in, + arg->off_in, + arg->nodeid_out, + arg->fh_out, + arg->off_out, + arg->len, + arg->flags); +} + +static +void +_debug_fuse_statx_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const struct fuse_statx_in *arg = (const fuse_statx_in*)arg_; + + fmt::print(output.get(), + "getattr_flags=0x{:08X}" + " fh=0x{:08X}" + " sx_flags=0x{:X}" + " sx_mask=0x{:X}" + "\n", + arg->getattr_flags, + arg->fh, + arg->sx_flags, + arg->sx_mask); +} + +static +void +_debug_fuse_tmpfile_in(const void *arg_) +{ + auto output = fuse_cfg.log_file(); + + const char *name; + const struct fuse_create_in *arg = (const fuse_create_in*)arg_; + + name = PARAM(arg); + + fmt::print(output.get(), + "mode={:o}" + " umask={:o}" + " name={}" + " flags=0x{:08X}" + " flags_str=\"{}\"" + "\n", + arg->mode, + arg->umask, + _quoted(name), + arg->flags, + _open_flags_str(arg->flags)); +} + +static void -debug_fuse_init_in(const struct fuse_init_in *arg_) +_debug_init_in(const struct fuse_init_in *arg_) { + auto output = fuse_cfg.log_file(); + uint64_t flags; flags = (((uint64_t)arg_->flags) | ((uint64_t)arg_->flags2) << 32); - fmt::print(g_OUTPUT, - "FUSE_INIT_IN: " - " major={};" - " minor={};" - " max_readahead={};" - " flags=0x%016lx (", + fmt::print(output.get(), + "major={}" + " minor={}" + " max_readahead={}" + " flags=0x{:016X}" + " flags_str=\"{}\"" + "\n", arg_->major, arg_->minor, arg_->max_readahead, - flags); - for(uint64_t i = 0; i < (sizeof(flags)*8); i++) - { - const char *str; - - if(!(flags & (1ULL << i))) - continue; - - str = fuse_flag_to_str(i); - if(str == NULL) - continue; - - fmt::print(g_OUTPUT,"%s, ",str); - } - fmt::print(g_OUTPUT,")\n"); + flags, + _fuse_init_flags_str(flags)); } void @@ -899,50 +1302,42 @@ fuse_syslog_fuse_init_out(const struct fuse_init_out *arg_) void -debug_fuse_init_out(const uint64_t unique_, +fuse_debug_init_out(const uint64_t unique_, const struct fuse_init_out *arg_, const uint64_t argsize_) { + auto output = fuse_cfg.log_file(); + uint64_t flags; const struct fuse_init_out *arg = arg_; flags = (((uint64_t)arg->flags) | ((uint64_t)arg->flags2) << 32); - fmt::print(g_OUTPUT, - "FUSE_INIT_OUT:" - " major={};" - " minor={};" - " max_readahead={};" - " flags=0x%016lx (" - , - /* unique_, */ - /* sizeof(struct fuse_out_header) + argsize_, */ + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " major={}" + " minor={}" + " max_readahead={}" + " flags=0x{:016X}" + " flags_str=\"{}\"" + " max_background={}" + " congestion_threshold={}" + " max_write={}" + " time_gran={}" + " max_pages={}" + " map_alignment={}" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + argsize_, arg->major, arg->minor, arg->max_readahead, - flags); - - for(uint64_t i = 0; i < (sizeof(flags)*8); i++) - { - const char *str; - - if(!(flags & (1ULL << i))) - continue; - - str = fuse_flag_to_str(i); - if(str == NULL) - continue; - - fmt::print(g_OUTPUT,"%s, ",str); - } - - fmt::print(g_OUTPUT, - "); max_background={};" - " congestion_threshold={};" - " max_write={};" - " time_gran={};" - " max_pages={};" - " map_alignment={};" - "\n", + flags, + _fuse_init_flags_str(flags), arg->max_background, arg->congestion_threshold, arg->max_write, @@ -953,26 +1348,26 @@ debug_fuse_init_out(const uint64_t unique_, static void -debug_fuse_attr(const struct fuse_attr *attr_) -{ - fmt::print(g_OUTPUT, - "attr:" - " ino=0x%016" PRIx64 ";" - " size={};" - " blocks={};" - " atime={};" - " atimensec={};" - " mtime={};" - " mtimensec={};" - " ctime={};" - " ctimesec={};" - " mode={:o};" - " nlink={};" - " uid={};" - " gid={};" - " rdev={};" - " blksize={};" - , +_debug_fuse_attr(const struct fuse_attr *attr_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ino=0x{:016X}" + " size={}" + " blocks={}" + " atime={}" + " atimensec={}" + " mtime={}" + " mtimensec={}" + " ctime={}" + " ctimensec={}" + " mode={:o}" + " nlink={}" + " uid={}" + " gid={}" + " rdev={}" + " blksize={}", attr_->ino, attr_->size, attr_->blocks, @@ -992,16 +1387,17 @@ debug_fuse_attr(const struct fuse_attr *attr_) static void -debug_fuse_entry(const struct fuse_entry_out *entry_) -{ - fmt::print(g_OUTPUT, - " fuse_entry_out:" - " nodeid=0x%016" PRIx64 ";" - " generation=0x%016" PRIx64 ";" - " entry_valid={};" - " entry_valid_nsec={};" - " attr_valid={};" - " attr_valid_nsec={};" +_debug_fuse_entry(const struct fuse_entry_out *entry_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "nodeid=0x{:016X}" + " generation=0x{:016X}" + " entry_valid={}" + " entry_valid_nsec={}" + " attr_valid={}" + " attr_valid_nsec={}" " ", entry_->nodeid, entry_->generation, @@ -1009,55 +1405,62 @@ debug_fuse_entry(const struct fuse_entry_out *entry_) entry_->entry_valid_nsec, entry_->attr_valid, entry_->attr_valid_nsec); - debug_fuse_attr(&entry_->attr); + _debug_fuse_attr(&entry_->attr); } void -debug_fuse_entry_out(const uint64_t unique_, +fuse_debug_entry_out(const uint64_t unique_, const struct fuse_entry_out *arg_, const uint64_t argsize_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " ", + _timestamp_ns(), unique_, sizeof(struct fuse_out_header) + argsize_); - debug_fuse_entry(arg_); - + _debug_fuse_entry(arg_); + fmt::print(output.get(),"\n"); } void -debug_fuse_attr_out(const uint64_t unique_, +fuse_debug_attr_out(const uint64_t unique_, const struct fuse_attr_out *arg_, const uint64_t argsize_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - "fuse_attr_out:" - " attr_valid={};" - " attr_valid_nsec={};" - " ino={};" - " size={};" - " blocks={};" - " atime={};" - " atimensec={};" - " mtime={};" - " mtimensec={};" - " ctime={};" - " ctimensec={};" - " mode={:o};" - " nlink={};" - " uid={};" - " gid={};" - " rdev={};" - " blksize={};" + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " attr_valid={}" + " attr_valid_nsec={}" + " ino=0x{:016X}" + " size={}" + " blocks={}" + " atime={}" + " atimensec={}" + " mtime={}" + " mtimensec={}" + " ctime={}" + " ctimensec={}" + " mode={:o}" + " nlink={}" + " uid={}" + " gid={}" + " rdev={}" + " blksize={}" "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_out_header) + argsize_, arg_->attr_valid, @@ -1081,32 +1484,33 @@ debug_fuse_attr_out(const uint64_t unique_, static void -debug_fuse_interrupt_in(const void *arg_) +_debug_fuse_interrupt_in(const void *arg_) { + auto output = fuse_cfg.log_file(); + const struct fuse_interrupt_in *arg = (const fuse_interrupt_in*)arg_; - fmt::print(g_OUTPUT, - "fuse_interrupt_in:" - " unique=0x%016" PRIx64 ";" - "\n" - , + fmt::print(output.get(), + "target_unique=0x{:016X}" + "\n", arg->unique); } static const char* -opcode_name(enum fuse_opcode op_) +_opcode_name(enum fuse_opcode op_) { static const char *names[] = { - "INVALID", + "RESERVED", "LOOKUP", "FORGET", "GETATTR", "SETATTR", "READLINK", "SYMLINK", + "RESERVED", "MKNOD", "MKDIR", "UNLINK", @@ -1118,6 +1522,7 @@ opcode_name(enum fuse_opcode op_) "WRITE", "STATFS", "RELEASE", + "RESERVED", "FSYNC", "SETXATTR", "GETXATTR", @@ -1147,29 +1552,40 @@ opcode_name(enum fuse_opcode op_) "LSEEK", "COPY_FILE_RANGE", "SETUPMAPPING", - "REMOVEMAPPING" + "REMOVEMAPPING", + "SYNCFS", + "TMPFILE", + "STATX" }; if(op_ >= (sizeof(names) / sizeof(names[0]))) - return "::UNKNOWN::"; + return "UNKNOWN"; return names[op_]; } void -debug_fuse_in_header(const struct fuse_in_header *hdr_) +fuse_debug_in_header(const struct fuse_in_header *hdr_) { const void *arg = &hdr_[1]; - - fmt::print(stderr, - "unique=0x%016" PRIx64 ";" - " opcode=%s ({});" - " nodeid={};" - " uid={};" - " gid={};" - " pid={}; || ", + auto output = fuse_cfg.log_file(); + + flockfile(output.get()); + + fmt::print(output.get(), + "ts={}" + " dir=IN" + " unique=0x{:016X}" + " opcode={}" + " opcode_id={}" + " nodeid={}" + " uid={}" + " gid={}" + " pid={}" + " ", + _timestamp_ns(), hdr_->unique, - opcode_name((fuse_opcode)hdr_->opcode), + _opcode_name((fuse_opcode)hdr_->opcode), hdr_->opcode, hdr_->nodeid, hdr_->uid, @@ -1179,185 +1595,260 @@ debug_fuse_in_header(const struct fuse_in_header *hdr_) switch(hdr_->opcode) { case FUSE_LOOKUP: - debug_fuse_lookup(arg); + _debug_fuse_lookup(arg); break; - case FUSE_INIT: - debug_fuse_init_in((const fuse_init_in*)arg); + case FUSE_FORGET: + _debug_fuse_forget_in(arg); break; case FUSE_GETATTR: - debug_fuse_getattr_in(arg); + _debug_fuse_getattr_in(arg); break; case FUSE_SETATTR: - debug_fuse_setattr_in(arg); + _debug_fuse_setattr_in(arg); break; - case FUSE_ACCESS: - debug_fuse_access_in(arg); - break; - case FUSE_MKNOD: - debug_fuse_mknod_in(arg); + case FUSE_READLINK: + fmt::print(output.get(),"\n"); break; - case FUSE_MKDIR: - debug_fuse_mkdir_in(arg); + case FUSE_SYMLINK: + _debug_fuse_symlink(arg); break; case FUSE_UNLINK: - debug_fuse_unlink(arg); + _debug_fuse_unlink(arg); break; case FUSE_RMDIR: - debug_fuse_rmdir(arg); + _debug_fuse_rmdir(arg); break; - case FUSE_SYMLINK: - debug_fuse_symlink(arg); + case FUSE_MKNOD: + _debug_fuse_mknod_in(arg); + break; + case FUSE_MKDIR: + _debug_fuse_mkdir_in(arg); break; case FUSE_RENAME: - debug_fuse_rename_in(arg); + _debug_fuse_rename_in(arg); break; case FUSE_LINK: - debug_fuse_link_in(arg); - break; - case FUSE_CREATE: - debug_fuse_create_in(arg); + _debug_fuse_link_in(arg); break; case FUSE_OPEN: - debug_fuse_open_in(arg); - break; - case FUSE_OPENDIR: - debug_fuse_open_in(arg); + _debug_fuse_open_in(arg); break; case FUSE_READ: - debug_fuse_read_in(arg); - break; - case FUSE_READDIR: - debug_fuse_read_in(arg); - break; - case FUSE_READDIRPLUS: - debug_fuse_read_in(arg); + _debug_fuse_read_in(arg); break; case FUSE_WRITE: - debug_fuse_write_in(arg); + _debug_fuse_write_in(arg); + break; + case FUSE_STATFS: + fmt::print(output.get(),"\n"); break; case FUSE_RELEASE: - debug_fuse_release_in(arg); + _debug_fuse_release_in(arg); break; - case FUSE_RELEASEDIR: - debug_fuse_release_in(arg); + case FUSE_FSYNC: + _debug_fuse_fsync_in(arg); break; - case FUSE_FSYNCDIR: - debug_fuse_fsync_in(arg); + case FUSE_SETXATTR: + _debug_fuse_setxattr_in(arg); break; case FUSE_GETXATTR: - debug_fuse_getxattr_in(arg); + _debug_fuse_getxattr_in(arg); break; case FUSE_LISTXATTR: - debug_fuse_listxattr(arg); - break; - case FUSE_SETXATTR: - debug_fuse_setxattr_in(arg); + _debug_fuse_listxattr(arg); break; case FUSE_REMOVEXATTR: - debug_fuse_removexattr(arg); - break; - case FUSE_FALLOCATE: - debug_fuse_fallocate_in(arg); + _debug_fuse_removexattr(arg); break; case FUSE_FLUSH: - debug_fuse_flush_in(arg); + _debug_fuse_flush_in(arg); + break; + case FUSE_OPENDIR: + _debug_fuse_open_in(arg); + break; + case FUSE_READDIR: + _debug_fuse_read_in(arg); + break; + case FUSE_RELEASEDIR: + _debug_fuse_release_in(arg); + break; + case FUSE_FSYNCDIR: + _debug_fuse_fsync_in(arg); + break; + case FUSE_GETLK: + _debug_fuse_lk_in(arg); + break; + case FUSE_SETLK: + _debug_fuse_lk_in(arg); + break; + case FUSE_SETLKW: + _debug_fuse_lk_in(arg); break; case FUSE_INTERRUPT: - debug_fuse_interrupt_in(arg); + _debug_fuse_interrupt_in(arg); + break; + case FUSE_BMAP: + _debug_fuse_bmap_in(arg); + break; + case FUSE_CREATE: + _debug_fuse_create_in(arg); + break; + case FUSE_IOCTL: + _debug_fuse_ioctl_in(arg); + break; + case FUSE_POLL: + _debug_fuse_poll_in(arg); + break; + case FUSE_BATCH_FORGET: + _debug_fuse_batch_forget_in(arg); + break; + case FUSE_FALLOCATE: + _debug_fuse_fallocate_in(arg); + break; + case FUSE_READDIRPLUS: + _debug_fuse_read_in(arg); + break; + case FUSE_RENAME2: + _debug_fuse_rename2_in(arg); + break; + case FUSE_LSEEK: + _debug_fuse_lseek_in(arg); + break; + case FUSE_COPY_FILE_RANGE: + _debug_fuse_copy_file_range_in(arg); + break; + case FUSE_TMPFILE: + _debug_fuse_tmpfile_in(arg); + break; + case FUSE_STATX: + _debug_fuse_statx_in(arg); + break; + case FUSE_ACCESS: + _debug_fuse_access_in(arg); + break; + case FUSE_INIT: + _debug_init_in((const fuse_init_in*)arg); break; default: - fmt::print(g_OUTPUT,"FIXME\n"); + fmt::print(output.get(),"unknown_opcode={}\n",hdr_->opcode); break; } + + funlockfile(output.get()); } void -debug_fuse_out_header(const struct fuse_out_header *hdr_) -{ - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=%d (%s);" - " len={};" - , +fuse_debug_out_header(const struct fuse_out_header *hdr_) +{ + auto output = fuse_cfg.log_file(); + + char strerror_buf[128]; + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error={}" + " error_str=\"{}\"" + " len={}" + "\n", + _timestamp_ns(), hdr_->unique, hdr_->error, - strerror(-hdr_->error), - sizeof(struct fuse_out_header)); + strerror_r(-hdr_->error,strerror_buf,sizeof(strerror_buf)), + hdr_->len); } void -debug_fuse_entry_open_out(const uint64_t unique_, +fuse_debug_entry_open_out(const uint64_t unique_, const struct fuse_entry_out *entry_, const struct fuse_open_out *open_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " ", + _timestamp_ns(), unique_, sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)); - debug_fuse_entry(entry_); - + _debug_fuse_entry(entry_); + fmt::print(output.get(), + " fh=0x{:08X}" + " open_flags=0x{:04X}" + " open_flags_str=\"{}\"" + "\n", + open_->fh, + open_->open_flags, + _fopen_flags_str(open_->open_flags)); } void -debug_fuse_readlink(const uint64_t unique_, +fuse_debug_readlink(const uint64_t unique_, const char *linkname_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - "readlink: linkname=%s" - "\n" - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " linkname={}" + "\n", + _timestamp_ns(), unique_, (sizeof(struct fuse_out_header) + strlen(linkname_)), - linkname_); + _quoted(linkname_)); } void -debug_fuse_write_out(const uint64_t unique_, +fuse_debug_write_out(const uint64_t unique_, const struct fuse_write_out *arg_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - " fuse_write_out:" + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" " size={}" - "\n" - , + "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_write_out), arg_->size); } void -debug_fuse_statfs_out(const uint64_t unique_, +fuse_debug_statfs_out(const uint64_t unique_, const struct fuse_statfs_out *arg_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - " fuse_statfs_out:" - " blocks={};" - " bfree={};" - " bavail={};" - " files={};" - " ffree={};" - " bsize={};" - " namelen={};" - " frsize={};" - "\n" - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " blocks={}" + " bfree={}" + " bavail={}" + " files={}" + " ffree={}" + " bsize={}" + " namelen={}" + " frsize={}" + "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_statfs_out), arg_->st.blocks, @@ -1371,40 +1862,43 @@ debug_fuse_statfs_out(const uint64_t unique_, } void -debug_fuse_getxattr_out(const uint64_t unique_, +fuse_debug_getxattr_out(const uint64_t unique_, const struct fuse_getxattr_out *arg_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - " fuse_getxattr_out:" - " size={};" - "\n" - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " size={}" + "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_out_header) + sizeof(struct fuse_getxattr_out), arg_->size); - } void -debug_fuse_lk_out(const uint64_t unique_, +fuse_debug_lk_out(const uint64_t unique_, const struct fuse_lk_out *arg_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - " fuse_file_lock:" - " start={};" - " end={};" - " type={};" - " pid={};" - "\n" - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " lk_start={}" + " lk_end={}" + " lk_type={}" + " lk_pid={}" + "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_out_header) + sizeof(struct fuse_lk_out), arg_->lk.start, @@ -1414,20 +1908,127 @@ debug_fuse_lk_out(const uint64_t unique_, } void -debug_fuse_bmap_out(const uint64_t unique_, +fuse_debug_bmap_out(const uint64_t unique_, const struct fuse_bmap_out *arg_) { - fmt::print(g_OUTPUT, - "unique=0x%016" PRIx64 ";" - " opcode=RESPONSE;" - " error=0 (Success);" - " len={}; || " - " fuse_bmap_out:" - " block={};" - "\n" - , + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " block={}" + "\n", + _timestamp_ns(), unique_, sizeof(struct fuse_out_header) + sizeof(struct fuse_bmap_out), arg_->block); +} + +void +fuse_debug_statx_out(const uint64_t unique_, + const struct fuse_statx_out *arg_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " attr_valid={}" + " attr_valid_nsec={}" + " flags=0x{:X}" + " ino=0x{:016X}" + " size={}" + " blocks={}" + " mode={:o}" + " nlink={}" + " uid={}" + " gid={}" + " blksize={}" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + sizeof(struct fuse_statx_out), + arg_->attr_valid, + arg_->attr_valid_nsec, + arg_->flags, + arg_->stat.ino, + arg_->stat.size, + arg_->stat.blocks, + arg_->stat.mode, + arg_->stat.nlink, + arg_->stat.uid, + arg_->stat.gid, + arg_->stat.blksize); +} + +void +fuse_debug_data_out(const uint64_t unique_, + const size_t size_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " data_size={}" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + size_, + size_); +} +void +fuse_debug_ioctl_out(const uint64_t unique_, + const struct fuse_ioctl_out *arg_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " result={}" + " flags=0x{:X}" + " in_iovs={}" + " out_iovs={}" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + sizeof(struct fuse_ioctl_out), + arg_->result, + arg_->flags, + arg_->in_iovs, + arg_->out_iovs); +} + +void +fuse_debug_poll_out(const uint64_t unique_, + const struct fuse_poll_out *arg_) +{ + auto output = fuse_cfg.log_file(); + + fmt::print(output.get(), + "ts={}" + " dir=OUT" + " unique=0x{:016X}" + " error=0" + " len={}" + " revents=0x{:X}" + "\n", + _timestamp_ns(), + unique_, + sizeof(struct fuse_out_header) + sizeof(struct fuse_poll_out), + arg_->revents); } diff --git a/libfuse/lib/fuse.cpp b/libfuse/lib/fuse.cpp index 6e4efec0..00e8c85b 100644 --- a/libfuse/lib/fuse.cpp +++ b/libfuse/lib/fuse.cpp @@ -14,6 +14,7 @@ #include "syslog.hpp" #include "crc32b.h" +#include "debug.hpp" #include "khash.h" #include "kvec.h" #include "mutex.hpp" @@ -67,8 +68,6 @@ #define PARAM(inarg) ((void*)(((char*)(inarg)) + sizeof(*(inarg)))) -static int g_LOG_METRICS = 0; - struct fuse_config { }; @@ -3752,7 +3751,7 @@ fuse_populate_maintenance_thread(struct fuse *f_) MaintenanceThread::push_job([=](int count_) { - if(g_LOG_METRICS) + if(fuse_cfg.debug) metrics_log_nodes_info_to_tmp_dir(f_); }); } @@ -3777,8 +3776,6 @@ fuse_new(struct fuse_chan *ch, if(fuse_opt_parse(args,&f.conf,fuse_lib_opts,fuse_lib_opt_proc) == -1) goto out_free_fs; - g_LOG_METRICS = fuse_cfg.debug; - f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop),&f); if(f.se == NULL) goto out_free_fs; @@ -3854,18 +3851,6 @@ fuse_destroy(struct fuse *) kv_destroy(f.remembered_nodes); } -void -fuse_log_metrics_set(int log_) -{ - g_LOG_METRICS = log_; -} - -int -fuse_log_metrics_get(void) -{ - return g_LOG_METRICS; -} - int fuse_passthrough_open(const int fd_) { diff --git a/libfuse/lib/fuse_cfg.cpp b/libfuse/lib/fuse_cfg.cpp index 01340252..574793f7 100644 --- a/libfuse/lib/fuse_cfg.cpp +++ b/libfuse/lib/fuse_cfg.cpp @@ -19,3 +19,28 @@ fuse_cfg_t::valid_umask() const { return (umask != FUSE_CFG_INVALID_UMASK); } + +std::shared_ptr +fuse_cfg_t::log_file() const +{ + return std::atomic_load(&_log_file); +} + +void +fuse_cfg_t::log_file(std::shared_ptr f_) +{ + std::atomic_store(&_log_file, f_); +} + +std::shared_ptr +fuse_cfg_t::log_filepath() const +{ + return std::atomic_load(&_log_filepath); +} + +void +fuse_cfg_t::log_filepath(const std::string &s_) +{ + std::atomic_store(&_log_filepath, + std::make_shared(s_)); +} diff --git a/libfuse/lib/fuse_lowlevel.cpp b/libfuse/lib/fuse_lowlevel.cpp index ad0df588..f8a1d8f3 100644 --- a/libfuse/lib/fuse_lowlevel.cpp +++ b/libfuse/lib/fuse_lowlevel.cpp @@ -240,6 +240,15 @@ int fuse_reply_err(fuse_req_t *req_, int err_) { + if(fuse_cfg.debug) + { + struct fuse_out_header hdr = {}; + hdr.unique = req_->ctx.unique; + hdr.error = (err_ > 0) ? -err_ : err_; + hdr.len = sizeof(struct fuse_out_header); + fuse_debug_out_header(&hdr); + } + return send_reply(req_,err_,NULL,0); } @@ -308,6 +317,9 @@ fuse_reply_entry(fuse_req_t *req, // the kernel. #endif + if(fuse_cfg.debug) + fuse_debug_entry_out(req->ctx.unique,&arg,size); + return send_reply_ok(req, &arg, size); } @@ -331,6 +343,9 @@ fuse_reply_create(fuse_req_t *req, fill_entry(earg, e); fill_open(oarg, f); + if(fuse_cfg.debug) + fuse_debug_entry_open_out(req->ctx.unique,earg,oarg); + return send_reply_ok(req, &buf, entrysize + sizeof(struct fuse_open_out)); } @@ -347,6 +362,9 @@ fuse_reply_attr(fuse_req_t *req, arg.attr_valid_nsec = 0; convert_stat(attr,&arg.attr); + if(fuse_cfg.debug) + fuse_debug_attr_out(req->ctx.unique,&arg,size); + return send_reply_ok(req,&arg,size); } @@ -363,6 +381,9 @@ fuse_reply_statx(fuse_req_t *req_, outarg.attr_valid_nsec = 0; outarg.stat = *(struct fuse_statx*)st_; + if(fuse_cfg.debug) + fuse_debug_statx_out(req_->ctx.unique,&outarg); + return send_reply_ok(req_,&outarg,sizeof(outarg)); } @@ -370,6 +391,9 @@ int fuse_reply_readlink(fuse_req_t *req, const char *linkname) { + if(fuse_cfg.debug) + fuse_debug_readlink(req->ctx.unique,linkname); + return send_reply_ok(req, linkname, strlen(linkname)); } @@ -381,6 +405,9 @@ fuse_reply_open(fuse_req_t *req, fill_open(&arg, f); + if(fuse_cfg.debug) + fuse_debug_open_out(req->ctx.unique,&arg,sizeof(arg)); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -392,6 +419,9 @@ fuse_reply_write(fuse_req_t *req, arg.size = count; + if(fuse_cfg.debug) + fuse_debug_write_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -400,6 +430,9 @@ fuse_reply_buf(fuse_req_t *req, const char *buf, size_t size) { + if(fuse_cfg.debug) + fuse_debug_data_out(req->ctx.unique,size); + return send_reply_ok(req, buf, size); } @@ -436,6 +469,9 @@ fuse_reply_data(fuse_req_t *req, out.unique = req->ctx.unique; out.error = 0; + if(fuse_cfg.debug) + fuse_debug_data_out(req->ctx.unique,bufsize_); + res = fuse_send_msg(req->f,req->ch,iov,2); if(res <= 0) { @@ -458,6 +494,9 @@ fuse_reply_statfs(fuse_req_t *req, convert_statfs(stbuf, &arg.st); + if(fuse_cfg.debug) + fuse_debug_statfs_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, size); } @@ -469,6 +508,9 @@ fuse_reply_xattr(fuse_req_t *req, arg.size = count; + if(fuse_cfg.debug) + fuse_debug_getxattr_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -489,6 +531,9 @@ fuse_reply_lock(fuse_req_t *req, } arg.lk.pid = lock->l_pid; + if(fuse_cfg.debug) + fuse_debug_lk_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -500,6 +545,9 @@ fuse_reply_bmap(fuse_req_t *req, arg.block = idx; + if(fuse_cfg.debug) + fuse_debug_bmap_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -541,6 +589,9 @@ fuse_reply_ioctl_retry(fuse_req_t *req, arg.flags |= FUSE_IOCTL_RETRY; arg.in_iovs = in_count; arg.out_iovs = out_count; + + if(fuse_cfg.debug) + fuse_debug_ioctl_out(req->ctx.unique,&arg); iov[count].iov_base = &arg; iov[count].iov_len = sizeof(arg); count++; @@ -619,6 +670,9 @@ fuse_reply_ioctl(fuse_req_t *req, arg.in_iovs = 0; arg.out_iovs = 0; + if(fuse_cfg.debug) + fuse_debug_ioctl_out(req->ctx.unique,&arg); + count = 1; iov[count].iov_base = &arg; iov[count].iov_len = sizeof(arg); @@ -649,6 +703,10 @@ fuse_reply_ioctl_iov(fuse_req_t *req, return fuse_reply_err(req, ENOMEM); arg.result = result; + + if(fuse_cfg.debug) + fuse_debug_ioctl_out(req->ctx.unique,&arg); + padded_iov[1].iov_base = &arg; padded_iov[1].iov_len = sizeof(arg); @@ -668,6 +726,9 @@ fuse_reply_poll(fuse_req_t *req, arg.revents = revents; + if(fuse_cfg.debug) + fuse_debug_poll_out(req->ctx.unique,&arg); + return send_reply_ok(req, &arg, sizeof(arg)); } @@ -1272,6 +1333,9 @@ do_init(fuse_req_t *req, fuse_syslog_fuse_init_out(&outarg); + if(fuse_cfg.debug) + fuse_debug_init_out(req->ctx.unique,&outarg,outargsize); + send_reply_ok(req, &outarg, outargsize); } @@ -1722,9 +1786,13 @@ fuse_send_errno(struct fuse_ll *f_, out.unique = unique_id_; out.error = -errno_; + out.len = sizeof(struct fuse_out_header); iov.iov_base = &out; iov.iov_len = sizeof(struct fuse_out_header); + if(fuse_cfg.debug) + fuse_debug_out_header(&out); + fuse_send_msg(f_,ch_,&iov,1); } @@ -1777,6 +1845,9 @@ fuse_ll_buf_process_read(struct fuse_session *se_, in = (struct fuse_in_header*)msgbuf_->mem; + if(fuse_cfg.debug) + fuse_debug_in_header(in); + req = fuse_ll_alloc_req(se_->f); if(req == NULL) return fuse_send_enomem(se_->f,se_->ch,in->unique); @@ -1817,6 +1888,9 @@ fuse_ll_buf_process_read_init(struct fuse_session *se_, in = (struct fuse_in_header*)msgbuf_->mem; + if(fuse_cfg.debug) + fuse_debug_in_header(in); + req = fuse_ll_alloc_req(se_->f); if(req == NULL) return fuse_send_enomem(se_->f,se_->ch,in->unique); diff --git a/mkdocs/docs/support.md b/mkdocs/docs/support.md index 8d9bd693..c943a3ff 100644 --- a/mkdocs/docs/support.md +++ b/mkdocs/docs/support.md @@ -24,32 +24,37 @@ directly.](mailto:support@spawn.link)** * Solution already ruled out and why. * The details from the output of the [mergerfs.collect-info](tooling.md#mergerfscollect-info) tool. -* Alternatively: - * Version of mergerfs: `mergerfs --version` - * mergerfs settings / arguments: from fstab, systemd unit, command - line, OMV plugin, etc. - * Version of the OS: `uname -a` and `lsb_release -a` - * List of branches, their filesystem types, sizes (before and after - issue): `df -h`, `lsblk -o NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA` -* **All** information about the relevant paths and files: permissions, ownership, etc. -* **All** information about the application making the requests: version, uid/gid +* **All** information about the relevant paths and files: permissions, + ownership, etc. Some of this is collected by the tool but only the + branch root details. +* **All** information about the application making the requests: + version, uid/gid. Try though to replicate any issues with common + core utilities. +* Try to recreate the problem in the simplest way using standard + programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple + mergerfs setup possible such as using only a single branch, well + supported branch filesystem, maybe reduce to using a single thread, + etc. * Runtime environment: * Is mergerfs running within a container? * Are the client apps using mergerfs running in a container? * A `strace` of the app having problems: * `strace -fvTtt -s 256 -o /tmp/app.strace.txt ` -* A `strace` of mergerfs **while** the program is trying to do - whatever it is failing to do: - * `strace -fvTtt -s 256 -p -o /tmp/mergerfs.strace.txt` -* **Precise** directions on replicating the issue. Do not leave - **anything** out. -* Try to recreate the problem in the simplest way using standard - programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple - mergerfs setup possible. +* A `strace` of mergerfs **before** and **while** the program is + trying to do whatever it is failing to do: + * `strace -fvTtt -s 256 -p -o + /tmp/mergerfs.strace.txt` +* If mergerfs is blocking or appears stuck: + * `gdb -q --batch -ex "set pagination off" -ex "thread apply all + bt full" -ex quit --pid > /tmp/mergerfs.stacktrace.txt` +* **Precise** step-by-step directions on replicating the issue. Do not + leave **anything** out. ### Contact / Issue submission +In order of preference. + * github.com: [https://github.com/trapexit/mergerfs/issues](https://github.com/trapexit/mergerfs/issues) * discord: [https://discord.gg/MpAr69V](https://discord.gg/MpAr69V) * reddit: [https://www.reddit.com/r/mergerfs](https://www.reddit.com/r/mergerfs) diff --git a/src/config.cpp b/src/config.cpp index 691dd2bc..59a28183 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -110,7 +110,7 @@ Config::Config() lazy_umount_mountpoint(false), link_cow(false), link_exdev(LinkEXDEV::ENUM::PASSTHROUGH), - log_metrics(false), + log_file({}), minfreespace(branches.minfreespace), mountpoint(), moveonenospc(true), @@ -137,7 +137,7 @@ Config::Config() xattr(XAttr::ENUM::PASSTHROUGH), _congestion_threshold(fuse_cfg.congestion_threshold), - _debug(fuse_cfg.debug), + _debug(false), _gid(fuse_cfg.gid), _max_background(fuse_cfg.max_background), _mount(mountpoint), @@ -260,7 +260,7 @@ Config::Config() _map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint; _map["link-cow"] = &link_cow; _map["link-exdev"] = &link_exdev; - _map["log.metrics"] = &log_metrics; + _map["log.file"] = &log_file; _map["minfreespace"] = &minfreespace; _map["mount"] = &_mount; _map["mountpoint"] = &_mountpoint; diff --git a/src/config.hpp b/src/config.hpp index c16e46d3..167e4e40 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -19,12 +19,13 @@ #include "branches.hpp" #include "category.hpp" #include "config_cachefiles.hpp" +#include "config_debug.hpp" #include "config_dummy.hpp" #include "config_flushonclose.hpp" #include "config_follow_symlinks.hpp" #include "config_inodecalc.hpp" #include "config_link_exdev.hpp" -#include "config_log_metrics.hpp" +#include "config_log_file.hpp" #include "config_moveonenospc.hpp" #include "config_nfsopenhack.hpp" #include "config_noforget.hpp" @@ -140,7 +141,7 @@ public: ConfigBOOL lazy_umount_mountpoint; ConfigBOOL link_cow; LinkEXDEV link_exdev; - LogMetrics log_metrics; + LogFile log_file; TFSRef minfreespace; fs::path mountpoint; MoveOnENOSPC moveonenospc; @@ -169,7 +170,7 @@ public: private: TFSRef _congestion_threshold; - TFSRef _debug; + Debug _debug; CfgDummy _dummy; TFSRef _gid; TFSRef _max_background; @@ -201,6 +202,7 @@ public: void keys_xattr(std::string &s) const; ssize_t keys_listxattr(char *list, size_t size) const; ssize_t keys_listxattr_size() const; + const Str2TFStrMap& get_map() const { return _map; } public: int get(const std::string &key, std::string *val) const; diff --git a/src/config_debug.cpp b/src/config_debug.cpp new file mode 100644 index 00000000..ee37ccf8 --- /dev/null +++ b/src/config_debug.cpp @@ -0,0 +1,63 @@ +/* + ISC License + + Copyright (c) 2026, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "config_debug.hpp" + +#include "debug.hpp" +#include "from_string.hpp" +#include "fuse_cfg.hpp" +#include "to_string.hpp" + +#include +#include + +Debug::Debug(const bool v_) +{ + fuse_cfg.debug = v_; +} + +std::string +Debug::to_string(void) const +{ + return str::to(fuse_cfg.debug); +} + +int +Debug::from_string(const std::string_view s_) +{ + int rv; + bool val; + + rv = str::from(s_,&val); + if(rv < 0) + return rv; + + fuse_cfg.debug = val; + + // If debug is being enabled, ensure output file is set + if(val) + { + auto log_file_path = fuse_cfg.log_filepath(); + if(log_file_path && !log_file_path->empty()) + fuse_debug_set_output(*log_file_path); + else + fuse_debug_set_output({}); // Use stderr + } + + return 0; +} diff --git a/src/config_debug.hpp b/src/config_debug.hpp new file mode 100644 index 00000000..1bee05d5 --- /dev/null +++ b/src/config_debug.hpp @@ -0,0 +1,31 @@ +/* + ISC License + + Copyright (c) 2026, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include "tofrom_string.hpp" + +class Debug : public ToFromString +{ +public: + Debug(const bool); + +public: + std::string to_string(void) const final; + int from_string(const std::string_view) final; +}; diff --git a/src/config_log_metrics.cpp b/src/config_log_file.cpp similarity index 65% rename from src/config_log_metrics.cpp rename to src/config_log_file.cpp index c63cff54..5f5c2b0c 100644 --- a/src/config_log_metrics.cpp +++ b/src/config_log_file.cpp @@ -16,38 +16,31 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config_log_metrics.hpp" -#include "from_string.hpp" -#include "to_string.hpp" +#include "config_log_file.hpp" -#include "fuse.h" +#include "debug.hpp" +#include "fuse_cfg.hpp" -LogMetrics::LogMetrics(const bool val_) +#include +#include + +LogFile::LogFile(const std::string &s_) { - fuse_log_metrics_set(val_); + fuse_debug_set_output(s_); } std::string -LogMetrics::to_string(void) const +LogFile::to_string(void) const { - bool val; - - val = fuse_log_metrics_get(); + auto filepath = fuse_cfg.log_filepath(); - return str::to(val); + return (filepath ? *filepath : ""); } int -LogMetrics::from_string(const std::string_view s_) +LogFile::from_string(const std::string_view s_) { - int rv; - bool val; - - rv = str::from(s_,&val); - if(rv < 0) - return rv; - - fuse_log_metrics_set(val); + const std::string s{s_}; - return 0; + return fuse_debug_set_output(s); } diff --git a/src/config_log_metrics.hpp b/src/config_log_file.hpp similarity index 93% rename from src/config_log_metrics.hpp rename to src/config_log_file.hpp index b7fa7d19..d9d8c1a3 100644 --- a/src/config_log_metrics.hpp +++ b/src/config_log_file.hpp @@ -20,10 +20,10 @@ #include "tofrom_string.hpp" -class LogMetrics : public ToFromString +class LogFile : public ToFromString { public: - LogMetrics(const bool); + LogFile(const std::string &); public: std::string to_string(void) const final; diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index 4f3f28c8..00136e21 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -233,5 +233,13 @@ FUSE::init(fuse_conn_info *conn_) SysLog::warning("passthrough and cache.writeback are incompatible."); } + SysLog::info("Config:"); + for(const auto &kv : cfg.get_map()) + { + if(not kv.second->display) + continue; + SysLog::info("{}={}",kv.first,kv.second->to_string()); + } + return NULL; } diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index f7167fb6..2bf8af93 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -315,12 +315,16 @@ _main(int argc_, SysLog::info("mergerfs v{} started",MERGERFS_VERSION); SysLog::info("Go to https://trapexit.github.io/mergerfs/latest/support for support"); + for(int i = 0; i < argc_; i++) + SysLog::info("argv[{}]: {}",i,argv_[i]); + options::parse(&args); + if(!cfg.errs.empty()) { for(auto &err : cfg.errs) { - std::string s = err.to_string(); + const std::string s = err.to_string(); SysLog::error("error: {}",s); fmt::println(stderr,"mergerfs: ERROR - {}",s); diff --git a/src/mergerfs_collect_info.cpp b/src/mergerfs_collect_info.cpp index 7bd870f6..65aa72e2 100644 --- a/src/mergerfs_collect_info.cpp +++ b/src/mergerfs_collect_info.cpp @@ -172,6 +172,12 @@ _lshw(const std::string &output_) ::_run({"lshw"},output_); } +static +void +_journalctl(const std::string &output_) +{ + ::_run({"journalctl","-t","mergerfs","--since","60 minutes ago"},output_); +} int mergerfs::collect_info::main(int argc_, @@ -208,6 +214,7 @@ mergerfs::collect_info::main(int argc_, ::_fstab(output_filepath); ::_software_versions(output_filepath); ::_lshw(output_filepath); + ::_journalctl(output_filepath); fmt::print("* Upload the following file to your" " GitHub ticket or put on https://pastebin.com"