diff --git a/ChangeLog.txt b/ChangeLog.txt index 5655f461..7ef4b3d9 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -19,7 +19,7 @@ Fixed bugs: * On Mac OS X, Finder shows the correct name for the mount directory -Version 0.9.10 (unreleased) +Version 0.9.10 -------------- Fixed bugs: * Fixed occasional deadlock (https://github.com/cryfs/cryfs/issues/64) diff --git a/appveyor.yml b/appveyor.yml index 6555400a..d5fa3de9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ platform: configuration: - Debug - RelWithDebInfo -# - Release + - Release version: '{branch}-{build}' @@ -20,13 +20,11 @@ init: - echo %NUMBER_OF_PROCESSORS% - echo %PLATFORM% - echo %APPVEYOR_BUILD_WORKER_IMAGE% -- set arch= -- if "%PLATFORM%"=="x64" ( set arch= Win64) -- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (set generator=Visual Studio 12 2013%arch%) -- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set generator=Visual Studio 14 2015%arch%) -- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set generator=Visual Studio 15 2017%arch%) -- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017 Preview" (set generator=Visual Studio 15 2017%arch%) -- echo %generator% +- set arch=32 +- if "%PLATFORM%"=="x64" ( set arch=64) +- set VisualStudioVersion=2017 +- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017 Preview" ( set VisualStudioVersion=Preview) +- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat" install: - choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1 @@ -35,17 +33,17 @@ install: build_script: - cmd: mkdir build - cmd: cd build - - cmd: cmake .. -G "%generator%" -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0" - # TODO Make build parallel + - cmd: cmake .. -G "Ninja" -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0" - cmd: cmake --build . --config %CONFIGURATION% - - cmd: .\test\gitversion\%CONFIGURATION%\gitversion-test.exe - - cmd: cd .\test\cpp-utils\%CONFIGURATION%\ && .\cpp-utils-test.exe && cd ..\..\.. - #- cmd: .\test\fspp\%CONFIGURATION%\fspp-test.exe - - cmd: .\test\parallelaccessstore\%CONFIGURATION%\parallelaccessstore-test.exe - - cmd: .\test\blockstore\%CONFIGURATION%\blockstore-test.exe - - cmd: .\test\blobstore\%CONFIGURATION%\blobstore-test.exe - - cmd: .\test\cryfs\%CONFIGURATION%\cryfs-test.exe - #- cmd: .\test\cryfs-cli\%CONFIGURATION%\cryfs-cli-test.exe + - cmd: .\test\gitversion\gitversion-test.exe + # cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that + - cmd: cd .\test\cpp-utils\ && .\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect && cd ..\.. + #- cmd: .\test\fspp\fspp-test.exe + - cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe + - cmd: .\test\blockstore\blockstore-test.exe + - cmd: .\test\blobstore\blobstore-test.exe + - cmd: .\test\cryfs\cryfs-test.exe + #- cmd: .\test\cryfs-cli\cryfs-cli-test.exe - cmd: cpack -C %CONFIGURATION% --verbose -G WIX diff --git a/src/blockstore/implementations/caching/CachingBlockStore2.cpp b/src/blockstore/implementations/caching/CachingBlockStore2.cpp index e9e9733c..b7437364 100644 --- a/src/blockstore/implementations/caching/CachingBlockStore2.cpp +++ b/src/blockstore/implementations/caching/CachingBlockStore2.cpp @@ -45,7 +45,7 @@ void CachingBlockStore2::CachedBlock::write(Data data) { } CachingBlockStore2::CachingBlockStore2(cpputils::unique_ref baseBlockStore) -: _baseBlockStore(std::move(baseBlockStore)), _cachedBlocksNotInBaseStoreMutex(), _cachedBlocksNotInBaseStore(), _cache() { +: _baseBlockStore(std::move(baseBlockStore)), _cachedBlocksNotInBaseStoreMutex(), _cachedBlocksNotInBaseStore(), _cache("blockstore") { } bool CachingBlockStore2::tryCreate(const BlockId &blockId, const Data &data) { diff --git a/src/blockstore/implementations/caching/cache/Cache.h b/src/blockstore/implementations/caching/cache/Cache.h index 588aa3b4..eb00dfa3 100644 --- a/src/blockstore/implementations/caching/cache/Cache.h +++ b/src/blockstore/implementations/caching/cache/Cache.h @@ -24,7 +24,7 @@ public: static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge static constexpr double MAX_LIFETIME_SEC = PURGE_LIFETIME_SEC + PURGE_INTERVAL; // This is the oldest age an entry can reach (given purging works in an ideal world, i.e. with the ideal interval and in zero time) - Cache(); + Cache(const std::string& cacheName); ~Cache(); uint32_t size() const; @@ -56,10 +56,10 @@ template constexpr double Cache constexpr double Cache::MAX_LIFETIME_SEC; template -Cache::Cache(): _mutex(), _currentlyFlushingEntries(), _cachedBlocks(), _timeoutFlusher(nullptr) { +Cache::Cache(const std::string& cacheName): _mutex(), _currentlyFlushingEntries(), _cachedBlocks(), _timeoutFlusher(nullptr) { //Don't initialize timeoutFlusher in the initializer list, //because it then might already call Cache::popOldEntries() before Cache is done constructing. - _timeoutFlusher = std::make_unique(std::bind(&Cache::_deleteOldEntriesParallel, this), PURGE_INTERVAL); + _timeoutFlusher = std::make_unique(std::bind(&Cache::_deleteOldEntriesParallel, this), PURGE_INTERVAL, "flush_" + cacheName); } template diff --git a/src/blockstore/implementations/caching/cache/PeriodicTask.cpp b/src/blockstore/implementations/caching/cache/PeriodicTask.cpp index 9145111f..ce4d68a9 100644 --- a/src/blockstore/implementations/caching/cache/PeriodicTask.cpp +++ b/src/blockstore/implementations/caching/cache/PeriodicTask.cpp @@ -7,10 +7,10 @@ using namespace cpputils::logging; namespace blockstore { namespace caching { -PeriodicTask::PeriodicTask(function task, double intervalSec) : +PeriodicTask::PeriodicTask(function task, double intervalSec, std::string threadName) : _task(task), _interval(static_cast(UINT64_C(1000000000) * intervalSec)), - _thread(std::bind(&PeriodicTask::_loopIteration, this)) { + _thread(std::bind(&PeriodicTask::_loopIteration, this), std::move(threadName)) { _thread.start(); } diff --git a/src/blockstore/implementations/caching/cache/PeriodicTask.h b/src/blockstore/implementations/caching/cache/PeriodicTask.h index 7663e293..eecdefce 100644 --- a/src/blockstore/implementations/caching/cache/PeriodicTask.h +++ b/src/blockstore/implementations/caching/cache/PeriodicTask.h @@ -11,7 +11,7 @@ namespace caching { class PeriodicTask final { public: - PeriodicTask(std::function task, double intervalSec); + PeriodicTask(std::function task, double intervalSec, std::string threadName); private: bool _loopIteration(); diff --git a/src/cpp-utils/CMakeLists.txt b/src/cpp-utils/CMakeLists.txt index 07ca7416..2d5db214 100644 --- a/src/cpp-utils/CMakeLists.txt +++ b/src/cpp-utils/CMakeLists.txt @@ -24,6 +24,8 @@ set(SOURCES io/pipestream.cpp thread/LoopThread.cpp thread/ThreadSystem.cpp + thread/debugging_nonwindows.cpp + thread/debugging_windows.cpp random/Random.cpp random/RandomGeneratorThread.cpp random/OSRandomGenerator.cpp diff --git a/src/cpp-utils/random/RandomGeneratorThread.cpp b/src/cpp-utils/random/RandomGeneratorThread.cpp index 418f7124..8d436dd5 100644 --- a/src/cpp-utils/random/RandomGeneratorThread.cpp +++ b/src/cpp-utils/random/RandomGeneratorThread.cpp @@ -8,7 +8,7 @@ namespace cpputils { _buffer(buffer), _minSize(minSize), _maxSize(maxSize), - _thread(std::bind(&RandomGeneratorThread::_loopIteration, this)) { + _thread(std::bind(&RandomGeneratorThread::_loopIteration, this), "RandomGeneratorThread") { ASSERT(_maxSize >= _minSize, "Invalid parameters"); } diff --git a/src/cpp-utils/thread/LoopThread.cpp b/src/cpp-utils/thread/LoopThread.cpp index 12489433..31227938 100644 --- a/src/cpp-utils/thread/LoopThread.cpp +++ b/src/cpp-utils/thread/LoopThread.cpp @@ -6,7 +6,8 @@ using boost::none; namespace cpputils { - LoopThread::LoopThread(function loopIteration): _loopIteration(std::move(loopIteration)), _runningHandle(none) { + LoopThread::LoopThread(function loopIteration, std::string threadName) + : _loopIteration(std::move(loopIteration)), _runningHandle(none), _threadName(std::move(threadName)) { } LoopThread::~LoopThread() { @@ -16,7 +17,7 @@ namespace cpputils { } void LoopThread::start() { - _runningHandle = ThreadSystem::singleton().start(_loopIteration); + _runningHandle = ThreadSystem::singleton().start(_loopIteration, _threadName); } void LoopThread::stop() { diff --git a/src/cpp-utils/thread/LoopThread.h b/src/cpp-utils/thread/LoopThread.h index a0cad1a9..ca4cefe1 100644 --- a/src/cpp-utils/thread/LoopThread.h +++ b/src/cpp-utils/thread/LoopThread.h @@ -14,7 +14,7 @@ namespace cpputils { class LoopThread final { public: // The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated. - LoopThread(std::function loopIteration); + LoopThread(std::function loopIteration, std::string threadName); ~LoopThread(); void start(); void stop(); @@ -22,6 +22,7 @@ namespace cpputils { private: std::function _loopIteration; boost::optional _runningHandle; + std::string _threadName; DISALLOW_COPY_AND_ASSIGN(LoopThread); }; diff --git a/src/cpp-utils/thread/ThreadSystem.cpp b/src/cpp-utils/thread/ThreadSystem.cpp index 4451a668..dc5a62a6 100644 --- a/src/cpp-utils/thread/ThreadSystem.cpp +++ b/src/cpp-utils/thread/ThreadSystem.cpp @@ -1,7 +1,9 @@ #include "ThreadSystem.h" #include "../logging/logging.h" +#include "debugging.h" using std::function; +using std::string; using namespace cpputils::logging; namespace cpputils { @@ -21,10 +23,10 @@ namespace cpputils { #endif } - ThreadSystem::Handle ThreadSystem::start(function loopIteration) { + ThreadSystem::Handle ThreadSystem::start(function loopIteration, string threadName) { boost::unique_lock lock(_mutex); - auto thread = _startThread(loopIteration); - _runningThreads.push_back(RunningThread{std::move(loopIteration), std::move(thread)}); + auto thread = _startThread(loopIteration, threadName); + _runningThreads.push_back(RunningThread{std::move(threadName), std::move(loopIteration), std::move(thread)}); return std::prev(_runningThreads.end()); } @@ -59,13 +61,14 @@ namespace cpputils { void ThreadSystem::_restartAllThreads() { for (RunningThread &thread : _runningThreads) { - thread.thread = _startThread(thread.loopIteration); + thread.thread = _startThread(thread.loopIteration, thread.threadName); } _mutex.unlock(); // Was locked in the before-fork handler } - boost::thread ThreadSystem::_startThread(function loopIteration) { - return boost::thread([loopIteration = std::move(loopIteration)] { + boost::thread ThreadSystem::_startThread(function loopIteration, const string& threadName) { + return boost::thread([loopIteration = std::move(loopIteration), threadName] { + cpputils::set_thread_name(threadName.c_str()); ThreadSystem::_runThread(loopIteration); }); } diff --git a/src/cpp-utils/thread/ThreadSystem.h b/src/cpp-utils/thread/ThreadSystem.h index b9a7b419..796a84d6 100644 --- a/src/cpp-utils/thread/ThreadSystem.h +++ b/src/cpp-utils/thread/ThreadSystem.h @@ -13,6 +13,7 @@ namespace cpputils { class ThreadSystem final { private: struct RunningThread { + std::string threadName; std::function loopIteration; // The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated. boost::thread thread; // boost::thread because we need it to be interruptible. }; @@ -21,7 +22,7 @@ namespace cpputils { static ThreadSystem &singleton(); - Handle start(std::function loopIteration); + Handle start(std::function loopIteration, std::string threadName); void stop(Handle handle); private: @@ -34,7 +35,7 @@ namespace cpputils { //TODO Rename to _doOnBeforeFork and _doAfterFork or similar, because they also handle locking _mutex for fork(). void _stopAllThreadsForRestart(); void _restartAllThreads(); - boost::thread _startThread(std::function loopIteration); + boost::thread _startThread(std::function loopIteration, const std::string& threadName); std::list _runningThreads; // std::list, because we give out iterators as handles boost::mutex _mutex; diff --git a/src/cpp-utils/thread/debugging.h b/src/cpp-utils/thread/debugging.h new file mode 100644 index 00000000..9965649c --- /dev/null +++ b/src/cpp-utils/thread/debugging.h @@ -0,0 +1,16 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DEBUGGING_H +#define MESSMER_CPPUTILS_DEBUGGING_H + +#include +#include + +namespace cpputils { + +void set_thread_name(const char* name); +std::string get_thread_name(); +std::string get_thread_name(std::thread* thread); + +} + +#endif diff --git a/src/cpp-utils/thread/debugging_nonwindows.cpp b/src/cpp-utils/thread/debugging_nonwindows.cpp new file mode 100644 index 00000000..3ed543ff --- /dev/null +++ b/src/cpp-utils/thread/debugging_nonwindows.cpp @@ -0,0 +1,55 @@ +#if !defined(_MSC_VER) + +#include "debugging.h" +#include +#include +#include +#include + +namespace cpputils { + +namespace { +constexpr size_t MAX_NAME_LEN = 16; // this length includes the terminating null character at the end +} + +void set_thread_name(const char* name) { + std::string name_(name); + if (name_.size() > MAX_NAME_LEN - 1) { + name_.resize(MAX_NAME_LEN - 1); + } +#if defined(__APPLE__) + int result = pthread_setname_np(name_.c_str()); +#else + int result = pthread_setname_np(pthread_self(), name_.c_str()); +#endif + if (0 != result) { + throw std::runtime_error("Error setting thread name with pthread_setname_np. Code: " + std::to_string(result)); + } +} + +namespace { +std::string get_thread_name(pthread_t thread) { + char name[MAX_NAME_LEN]; + int result = pthread_getname_np(thread, name, MAX_NAME_LEN); + if (0 != result) { + throw std::runtime_error("Error getting thread name with pthread_getname_np. Code: " + std::to_string(result)); + } + // pthread_getname_np returns a null terminated string with maximum 16 bytes. + // but just to be safe against a buggy implementation, let's set the last byte to zero. + name[MAX_NAME_LEN - 1] = '\0'; + return name; +} +} + +std::string get_thread_name() { + return get_thread_name(pthread_self()); +} + +std::string get_thread_name(std::thread* thread) { + ASSERT(thread->joinable(), "Thread not running"); + return get_thread_name(thread->native_handle()); +} + +} + +#endif diff --git a/src/cpp-utils/thread/debugging_windows.cpp b/src/cpp-utils/thread/debugging_windows.cpp new file mode 100644 index 00000000..4033b638 --- /dev/null +++ b/src/cpp-utils/thread/debugging_windows.cpp @@ -0,0 +1,111 @@ +#if defined(_MSC_VER) + +#include +#include "debugging.h" +#include +#include + +using std::string; +using std::wstring; +using std::wstring_convert; + +namespace cpputils { + +namespace { +struct NameData final { + wchar_t *name = nullptr; + + ~NameData() { + if (nullptr != LocalFree(name)) { + throw std::runtime_error("Error releasing thread description memory. Error code: " + std::to_string(GetLastError())); + } + } +}; + +struct ModuleHandle final { + HMODULE module; + + ModuleHandle(const char* dll) { + bool success = GetModuleHandleExA(0, dll, &module); + if (!success) { + throw std::runtime_error(string() + "Error loading dll: " + dll + ". Error code: " + std::to_string(GetLastError())); + } + } + + ~ModuleHandle() { + bool success = FreeLibrary(module); + if (!success) { + throw std::runtime_error("Error unloading dll. Error code: " + std::to_string(GetLastError())); + } + } +}; +template +class APIFunction final { +private: + ModuleHandle module_; + Fn func_; + +public: + APIFunction(const char* dll, const char* function) + : module_(dll), func_(reinterpret_cast(GetProcAddress(module_.module, function))) { + } + + bool valid() const { + return func_ != nullptr; + } + + Fn func() const { + return func_; + } +}; + +std::string get_thread_name(HANDLE thread) { + // The GetThreadDescription API was brought in version 1607 of Windows 10. + typedef HRESULT(WINAPI* GetThreadDescriptionFn)(HANDLE hThread, PWSTR* ppszThreadDescription); + static APIFunction get_thread_description_func("Kernel32.dll", "GetThreadDescription"); + + if (get_thread_description_func.valid()) { + NameData name_data; + + HRESULT status = get_thread_description_func.func()(thread, &name_data.name); + if (FAILED(status)) { + throw std::runtime_error("Error getting thread description. Error code: " + std::to_string(status)); + } + return wstring_convert>().to_bytes(name_data.name); + } + else { + // GetThreadDescription API is not available. + return ""; + } +} +} + +void set_thread_name(const char* name) { + // The GetThreadDescription API was brought in version 1607 of Windows 10. + typedef HRESULT(WINAPI* SetThreadDescriptionFn)(HANDLE hThread, PCWSTR lpThreadDescription); + static APIFunction set_thread_description_func("Kernel32.dll", "SetThreadDescription"); + + if (set_thread_description_func.valid()) { + wstring wname = wstring_convert>().from_bytes(name); + HRESULT status = set_thread_description_func.func()(GetCurrentThread(), wname.c_str()); + if (FAILED(status)) { + throw std::runtime_error("Error setting thread description. Error code: " + std::to_string(status)); + } + } + else { + // intentionally empty. SetThreadDescription API is not available. + } +} + +std::string get_thread_name() { + return get_thread_name(GetCurrentThread()); +} + +std::string get_thread_name(std::thread* thread) { + ASSERT(thread->joinable(), "Thread not running"); + return get_thread_name(static_cast(thread->native_handle())); +} + +} + +#endif diff --git a/src/cryfs-cli/CallAfterTimeout.h b/src/cryfs-cli/CallAfterTimeout.h index 16cfe48c..6052a38c 100644 --- a/src/cryfs-cli/CallAfterTimeout.h +++ b/src/cryfs-cli/CallAfterTimeout.h @@ -9,7 +9,7 @@ namespace cryfs_cli { class CallAfterTimeout final { public: - CallAfterTimeout(boost::chrono::milliseconds timeout, std::function callback); + CallAfterTimeout(boost::chrono::milliseconds timeout, std::function callback, const std::string& timeoutName); void resetTimer(); private: bool _checkTimeoutThreadIteration(); @@ -25,8 +25,8 @@ namespace cryfs_cli { DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout); }; - inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function callback) - :_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this)) { + inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function callback, const std::string& timeoutName) + :_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this), "timeout_" + timeoutName) { resetTimer(); _checkTimeoutThread.start(); } diff --git a/src/cryfs-cli/Cli.cpp b/src/cryfs-cli/Cli.cpp index 8f3dd291..b4e7a508 100644 --- a/src/cryfs-cli/Cli.cpp +++ b/src/cryfs-cli/Cli.cpp @@ -26,6 +26,7 @@ #include #include "Environment.h" #include +#include //TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member. //TODO Factor out class handling askPassword @@ -297,7 +298,7 @@ namespace cryfs_cli { return none; } uint64_t millis = std::llround(60000 * (*minutes)); - return make_unique_ref(milliseconds(millis), callback); + return make_unique_ref(milliseconds(millis), callback, "idlecallback"); } void Cli::_initLogfile(const ProgramOptions &options) { @@ -396,6 +397,7 @@ namespace cryfs_cli { int Cli::main(int argc, const char *argv[], unique_ref httpClient, std::function onMounted) { cpputils::showBacktraceOnCrash(); + cpputils::set_thread_name("cryfs"); try { _showVersion(std::move(httpClient)); diff --git a/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h b/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h index 6bba337a..5e75131f 100644 --- a/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h +++ b/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h @@ -50,7 +50,7 @@ namespace cryfs { inline CachingFsBlobStore::CachingFsBlobStore(cpputils::unique_ref baseBlobStore) - : _baseBlobStore(std::move(baseBlobStore)), _cache() { + : _baseBlobStore(std::move(baseBlobStore)), _cache("fsblobstore") { } inline CachingFsBlobStore::~CachingFsBlobStore() { diff --git a/src/fspp/fuse/Fuse.cpp b/src/fspp/fuse/Fuse.cpp index 68489964..6303b28b 100644 --- a/src/fspp/fuse/Fuse.cpp +++ b/src/fspp/fuse/Fuse.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "InvalidFilesystem.h" @@ -23,7 +24,9 @@ namespace bf = boost::filesystem; using namespace cpputils::logging; using std::make_shared; using std::shared_ptr; +using std::string; using namespace fspp::fuse; +using cpputils::set_thread_name; namespace { bool is_valid_fspp_path(const bf::path& path) { @@ -32,6 +35,18 @@ bool is_valid_fspp_path(const bf::path& path) { && !path.has_root_name() // on Windows, it shouldn't have a device specifier (i.e. no "C:") && (path.string() == path.generic_string()); // must use portable '/' as directory separator } + +class ThreadNameForDebugging final { +public: + ThreadNameForDebugging(const string& threadName) { + std::string name = "fspp_" + threadName; + set_thread_name(name.c_str()); + } + + ~ThreadNameForDebugging() { + set_thread_name("fspp_idle"); + } +}; } #define FUSE_OBJ (static_cast(fuse_get_context()->private_data)) @@ -335,6 +350,7 @@ void Fuse::unmount(const bf::path& mountdir, bool force) { } int Fuse::getattr(const bf::path &path, fspp::fuse::STAT *stbuf) { + ThreadNameForDebugging _threadName("getattr"); #ifdef FSPP_LOG LOG(DEBUG, "getattr({}, _, _)", path); #endif @@ -357,6 +373,7 @@ int Fuse::getattr(const bf::path &path, fspp::fuse::STAT *stbuf) { } int Fuse::fgetattr(const bf::path &path, fspp::fuse::STAT *stbuf, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("fgetattr"); #ifdef FSPP_LOG LOG(DEBUG, "fgetattr({}, _, _)\n", path); #endif @@ -389,6 +406,7 @@ int Fuse::fgetattr(const bf::path &path, fspp::fuse::STAT *stbuf, fuse_file_info } int Fuse::readlink(const bf::path &path, char *buf, size_t size) { + ThreadNameForDebugging _threadName("readlink"); #ifdef FSPP_LOG LOG(DEBUG, "readlink({}, _, {})", path, size); #endif @@ -414,11 +432,13 @@ int Fuse::mknod(const bf::path &path, ::mode_t mode, dev_t rdev) { UNUSED(rdev); UNUSED(mode); UNUSED(path); + ThreadNameForDebugging _threadName("mknod"); LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode); return ENOSYS; } int Fuse::mkdir(const bf::path &path, ::mode_t mode) { + ThreadNameForDebugging _threadName("mkdir"); #ifdef FSPP_LOG LOG(DEBUG, "mkdir({}, {})", path, mode); #endif @@ -442,6 +462,7 @@ int Fuse::mkdir(const bf::path &path, ::mode_t mode) { } int Fuse::unlink(const bf::path &path) { + ThreadNameForDebugging _threadName("unlink"); #ifdef FSPP_LOG LOG(DEBUG, "unlink({})", path); #endif @@ -464,6 +485,7 @@ int Fuse::unlink(const bf::path &path) { } int Fuse::rmdir(const bf::path &path) { + ThreadNameForDebugging _threadName("rmdir"); #ifdef FSPP_LOG LOG(DEBUG, "rmdir({})", path); #endif @@ -486,6 +508,7 @@ int Fuse::rmdir(const bf::path &path) { } int Fuse::symlink(const bf::path &to, const bf::path &from) { + ThreadNameForDebugging _threadName("symlink"); #ifdef FSPP_LOG LOG(DEBUG, "symlink({}, {})", to, from); #endif @@ -509,6 +532,7 @@ int Fuse::symlink(const bf::path &to, const bf::path &from) { } int Fuse::rename(const bf::path &from, const bf::path &to) { + ThreadNameForDebugging _threadName("rename"); #ifdef FSPP_LOG LOG(DEBUG, "rename({}, {})", from, to); #endif @@ -533,6 +557,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) { //TODO int Fuse::link(const bf::path &from, const bf::path &to) { + ThreadNameForDebugging _threadName("link"); LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to); //auto real_from = _impl->RootDir() / from; //auto real_to = _impl->RootDir() / to; @@ -542,6 +567,7 @@ int Fuse::link(const bf::path &from, const bf::path &to) { } int Fuse::chmod(const bf::path &path, ::mode_t mode) { + ThreadNameForDebugging _threadName("chmod"); #ifdef FSPP_LOG LOG(DEBUG, "chmod({}, {})", path, mode); #endif @@ -564,6 +590,7 @@ int Fuse::chmod(const bf::path &path, ::mode_t mode) { } int Fuse::chown(const bf::path &path, ::uid_t uid, ::gid_t gid) { + ThreadNameForDebugging _threadName("chown"); #ifdef FSPP_LOG LOG(DEBUG, "chown({}, {}, {})", path, uid, gid); #endif @@ -586,6 +613,7 @@ int Fuse::chown(const bf::path &path, ::uid_t uid, ::gid_t gid) { } int Fuse::truncate(const bf::path &path, int64_t size) { + ThreadNameForDebugging _threadName("truncate"); #ifdef FSPP_LOG LOG(DEBUG, "truncate({}, {})", path, size); #endif @@ -608,6 +636,7 @@ int Fuse::truncate(const bf::path &path, int64_t size) { } int Fuse::ftruncate(const bf::path &path, int64_t size, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("ftruncate"); #ifdef FSPP_LOG LOG(DEBUG, "ftruncate({}, {})", path, size); #endif @@ -630,6 +659,7 @@ int Fuse::ftruncate(const bf::path &path, int64_t size, fuse_file_info *fileinfo } int Fuse::utimens(const bf::path &path, const timespec times[2]) { + ThreadNameForDebugging _threadName("utimens"); #ifdef FSPP_LOG LOG(DEBUG, "utimens({}, _)", path); #endif @@ -652,6 +682,7 @@ int Fuse::utimens(const bf::path &path, const timespec times[2]) { } int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("open"); #ifdef FSPP_LOG LOG(DEBUG, "open({}, _)", path); #endif @@ -674,6 +705,7 @@ int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { } int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("release"); #ifdef FSPP_LOG LOG(DEBUG, "release({}, _)", path); #endif @@ -696,6 +728,7 @@ int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { } int Fuse::read(const bf::path &path, char *buf, size_t size, int64_t offset, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("read"); #ifdef FSPP_LOG LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset); #endif @@ -717,6 +750,7 @@ int Fuse::read(const bf::path &path, char *buf, size_t size, int64_t offset, fus } int Fuse::write(const bf::path &path, const char *buf, size_t size, int64_t offset, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("write"); #ifdef FSPP_LOG LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset); #endif @@ -739,6 +773,7 @@ int Fuse::write(const bf::path &path, const char *buf, size_t size, int64_t offs } int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) { + ThreadNameForDebugging _threadName("statfs"); #ifdef FSPP_LOG LOG(DEBUG, "statfs({}, _)", path); #endif @@ -762,6 +797,7 @@ int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) { } int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("flush"); #ifdef FSPP_LOG LOG(WARN, "flush({}, _)", path); #endif @@ -784,6 +820,7 @@ int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { } int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("fsync"); #ifdef FSPP_LOG LOG(DEBUG, "fsync({}, {}, _)", path, datasync); #endif @@ -812,12 +849,14 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) { UNUSED(path); UNUSED(fileinfo); + ThreadNameForDebugging _threadName("opendir"); //LOG(DEBUG, "opendir({}, _)", path); //We don't need opendir, because readdir works directly on the path return 0; } int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, int64_t offset, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("readdir"); #ifdef FSPP_LOG LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest); #endif @@ -863,6 +902,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, int64 int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) { UNUSED(path); UNUSED(fileinfo); + ThreadNameForDebugging _threadName("releasedir"); //LOG(DEBUG, "releasedir({}, _)", path); //We don't need releasedir, because readdir works directly on the path return 0; @@ -873,12 +913,14 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo) UNUSED(fileinfo); UNUSED(datasync); UNUSED(path); + ThreadNameForDebugging _threadName("fsyncdir"); //LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync); return 0; } void Fuse::init(fuse_conn_info *conn) { UNUSED(conn); + ThreadNameForDebugging _threadName("init"); _fs = _init(this); LOG(INFO, "Filesystem started."); @@ -892,6 +934,7 @@ void Fuse::init(fuse_conn_info *conn) { } void Fuse::destroy() { + ThreadNameForDebugging _threadName("destroy"); _fs = make_shared(); LOG(INFO, "Filesystem stopped."); _running = false; @@ -899,6 +942,7 @@ void Fuse::destroy() { } int Fuse::access(const bf::path &path, int mask) { + ThreadNameForDebugging _threadName("access"); #ifdef FSPP_LOG LOG(DEBUG, "access({}, {})", path, mask); #endif @@ -921,6 +965,7 @@ int Fuse::access(const bf::path &path, int mask) { } int Fuse::create(const bf::path &path, ::mode_t mode, fuse_file_info *fileinfo) { + ThreadNameForDebugging _threadName("create"); #ifdef FSPP_LOG LOG(DEBUG, "create({}, {}, _)", path, mode); #endif diff --git a/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp b/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp index e69a278b..c4377ab0 100644 --- a/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp +++ b/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp @@ -13,7 +13,7 @@ using ::testing::Test; //Test that Cache uses a move constructor for Value if possible class CacheTest_MoveConstructor: public Test { public: - CacheTest_MoveConstructor(): cache(make_unique_ref>()) { + CacheTest_MoveConstructor(): cache(make_unique_ref>("test")) { CopyableMovableValueType::numCopyConstructorCalled = 0; } unique_ref> cache; diff --git a/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp b/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp index 7bc76c3b..d41fd3cc 100644 --- a/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp +++ b/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp @@ -36,7 +36,7 @@ private: class CacheTest_RaceCondition: public ::testing::Test { public: - CacheTest_RaceCondition(): cache(), destructorStarted(), destructorFinished(false) {} + CacheTest_RaceCondition(): cache("test"), destructorStarted(), destructorFinished(false) {} static constexpr unsigned int MAX_ENTRIES = 100; diff --git a/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp b/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp index 46843015..5dc7c739 100644 --- a/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp +++ b/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp @@ -37,7 +37,7 @@ class PeriodicTaskTest: public Test { }; TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) { - PeriodicTask task([](){}, 1); + PeriodicTask task([](){}, 1, "test"); } TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) { @@ -45,7 +45,7 @@ TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) { PeriodicTask task([&counter](){ counter.decrease(); - }, 0.001); + }, 0.001, "test"); counter.waitForZero(); } @@ -55,7 +55,7 @@ TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) { { PeriodicTask task([&callCount](){ callCount += 1; - }, 0.001); + }, 0.001, "test"); } int callCountDirectlyAfterDestruction = callCount; boost::this_thread::sleep_for(boost::chrono::seconds(1)); diff --git a/test/blockstore/implementations/caching/cache/testutils/CacheTest.h b/test/blockstore/implementations/caching/cache/testutils/CacheTest.h index f4c520b1..937609c9 100644 --- a/test/blockstore/implementations/caching/cache/testutils/CacheTest.h +++ b/test/blockstore/implementations/caching/cache/testutils/CacheTest.h @@ -13,7 +13,7 @@ // Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values). class CacheTest: public ::testing::Test { public: - CacheTest(): _cache() {} + CacheTest(): _cache("test") {} void push(int key, int value); boost::optional pop(int key); diff --git a/test/cpp-utils/CMakeLists.txt b/test/cpp-utils/CMakeLists.txt index 9997b380..5c027588 100644 --- a/test/cpp-utils/CMakeLists.txt +++ b/test/cpp-utils/CMakeLists.txt @@ -54,6 +54,7 @@ set(SOURCES system/MemoryTest.cpp system/HomedirTest.cpp system/EnvTest.cpp + thread/debugging_test.cpp value_type/ValueTypeTest.cpp either_test.cpp ) diff --git a/test/cpp-utils/thread/debugging_test.cpp b/test/cpp-utils/thread/debugging_test.cpp new file mode 100644 index 00000000..7df169ef --- /dev/null +++ b/test/cpp-utils/thread/debugging_test.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +using namespace cpputils; +using std::string; + +TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenSettingAndGetting_thenDoesntCrash) { + set_thread_name("my_thread_name"); + get_thread_name(); +} + +TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_thenDoesntCrash) { + ConditionBarrier nameIsChecked; + + bool child_didnt_crash = false; + std::thread child([&] { + set_thread_name("my_thread_name"); + get_thread_name(); + child_didnt_crash = true; + nameIsChecked.wait(); + }); + get_thread_name(&child); + nameIsChecked.release(); // getting the name of a not-running thread would cause errors, so let's make sure we only exit after getting the name + child.join(); + EXPECT_TRUE(child_didnt_crash); +} + +TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) { + set_thread_name("my_thread_name"); + string name = get_thread_name(); + EXPECT_EQ("my_thread_name", name); +} + +TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) { + std::thread child([] { + set_thread_name("my_thread_name"); + string name = get_thread_name(); + EXPECT_EQ("my_thread_name", name); + }); + child.join(); +} + +TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromOutside_thenIsCorrect) { + ConditionBarrier nameIsSet; + ConditionBarrier nameIsChecked; + + std::thread child([&] { + set_thread_name("my_thread_name"); + nameIsSet.release(); + nameIsChecked.wait(); + }); + + nameIsSet.wait(); + string name = get_thread_name(&child); + EXPECT_EQ("my_thread_name", name); + + nameIsChecked.release(); + child.join(); +} + diff --git a/test/cryfs-cli/CallAfterTimeoutTest.cpp b/test/cryfs-cli/CallAfterTimeoutTest.cpp index 2ba7eec1..5d11ed0c 100644 --- a/test/cryfs-cli/CallAfterTimeoutTest.cpp +++ b/test/cryfs-cli/CallAfterTimeoutTest.cpp @@ -15,7 +15,7 @@ public: CallAfterTimeoutTest(): called(false) {} unique_ref callAfterTimeout(milliseconds timeout) { - return make_unique_ref(timeout, [this] {called = true;}); + return make_unique_ref(timeout, [this] {called = true;}, "test"); } std::atomic called;