diff --git a/src/add-ons/kernel/file_systems/nfs4/Debug.cpp b/src/add-ons/kernel/file_systems/nfs4/Debug.cpp
new file mode 100644
index 0000000000..7ff946f4dc
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/nfs4/Debug.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2025, Haiku, Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include "Debug.h"
+#include "FileSystem.h"
+#include "VnodeToInode.h"
+
+
+int
+kprintf_volume(int argc, char** argv)
+{
+ if ((argc == 1) || strcmp(argv[1], "--help") == 0) {
+ kprintf("usage: nfs4
\n"
+ " address: address of a nfs4 private volume (FileSystem)\n"
+ " Use 'mounts' to list mounted volume ids, and 'mount ' to display a private "
+ "volume address.\n");
+ return 0;
+ }
+
+ FileSystem* volume = reinterpret_cast(strtoul(argv[1], NULL, 0));
+ volume->Dump(kprintf);
+
+ return 0;
+}
+
+
+int
+kprintf_inode(int argc, char** argv)
+{
+ if ((argc == 1) || strcmp(argv[1], "--help") == 0) {
+ kprintf("usage: nfs4_inode \n"
+ " address(es): address of one or more nfs4 private nodes (VnodeToInode), "
+ "separated by spaces\n"
+ " Addresses can be found with the 'vnodes' command.\n");
+ return 0;
+ }
+
+ for (int i = 1; i < argc; i++) {
+ VnodeToInode* node = reinterpret_cast(strtoul(argv[1], NULL, 0));
+ if (node == NULL)
+ continue;
+ node->Dump(kprintf);
+ kprintf("----------\n");
+ }
+
+ return 0;
+}
diff --git a/src/add-ons/kernel/file_systems/nfs4/Debug.h b/src/add-ons/kernel/file_systems/nfs4/Debug.h
new file mode 100644
index 0000000000..0ff9ca3f99
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/nfs4/Debug.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025, Haiku, Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef NFS4_DEBUG_H
+#define NFS4_DEBUG_H
+
+
+#ifdef USER
+#define _KERNEL_MODE
+ // skip the POSIX dprintf declaration in stdio.h
+#include
+#undef _KERNEL_MODE
+#endif
+
+#include
+
+
+#ifdef DEBUG
+#define TRACE(x...) FUNCTION(x)
+#define CALLED() FUNCTION_START()
+#else
+#define TRACE(x...)
+#define CALLED()
+#endif
+
+#if USER
+extern "C" void dprintf(const char *format, ...);
+#endif
+int kprintf_volume(int argc, char** argv);
+int kprintf_inode(int argc, char** argv);
+
+
+#endif // NFS4_DEBUG_H
diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp
index fab2d20d0d..c3c3ae6336 100644
--- a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp
@@ -256,6 +256,35 @@ DirectoryCache::Revalidate()
}
+void
+DirectoryCache::Dump(void (*xprintf)(const char*, ...))
+{
+ MutexLocker locker;
+ if (xprintf != kprintf)
+ locker.SetTo(fLock, false);
+
+ _DumpLocked(xprintf);
+
+ return;
+}
+
+
+void
+DirectoryCache::_DumpLocked(void (*xprintf)(const char*, ...)) const
+{
+ xprintf("DirectoryCache::fNameCache:\n");
+
+ for (SinglyLinkedList::ConstIterator it = fNameCache.GetIterator();
+ const NameCacheEntry* entry = it.Next();) {
+ xprintf("\t\tino: %" B_PRIdINO "\t", entry->fNode);
+ if (entry->fName != NULL)
+ xprintf("name: %s\n", entry->fName);
+ }
+
+ return;
+}
+
+
void
DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
DirectoryCacheSnapshot* newSnapshot)
diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h
index f7240d921c..7943246031 100644
--- a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h
+++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h
@@ -15,6 +15,8 @@
#include
#include
+#include "Debug.h"
+
class Inode;
@@ -66,7 +68,10 @@ public:
inline Inode* GetInode();
+ void Dump(void (*xprintf)(const char*, ...) = dprintf);
+
const bigtime_t fExpirationTime;
+
protected:
void NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
DirectoryCacheSnapshot* newSnapshot);
@@ -75,6 +80,8 @@ private:
void _SetSnapshot(DirectoryCacheSnapshot* snapshot);
status_t _LoadSnapshot(bool trash);
+ void _DumpLocked(void (*xprintf)(const char*, ...)) const;
+
SinglyLinkedList fNameCache;
DirectoryCacheSnapshot* fDirectoryCache;
diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp
index 45f002d202..16916e4f3b 100644
--- a/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp
@@ -13,6 +13,18 @@
#include "Request.h"
+/*! Print the handle.
+ @pre The parent object is locked.
+*/
+void
+FileHandle::Dump(void (*xprintf)(const char*, ...)) const
+{
+ for (int i = 0; i < fSize; ++i)
+ xprintf("%d ", fData[i]);
+ xprintf("\n");
+}
+
+
InodeName::InodeName(InodeNames* parent, const char* name)
:
fParent(parent),
@@ -87,6 +99,33 @@ InodeNames::RemoveName(InodeNames* parent, const char* name)
}
+void
+InodeNames::Dump(void (*xprintf)(const char*, ...))
+{
+ MutexLocker locker;
+ if (xprintf != kprintf)
+ locker.SetTo(fLock, false);
+
+ _DumpLocked(xprintf);
+
+ return;
+}
+
+
+void
+InodeNames::_DumpLocked(void (*xprintf)(const char*, ...)) const
+{
+ for (SinglyLinkedList::ConstIterator it = fNames.GetIterator();
+ const InodeName* name = it.Next();) {
+ if (name->fName != NULL)
+ xprintf("%s ", name->fName);
+ }
+ xprintf("\n");
+
+ return;
+}
+
+
FileInfo::FileInfo()
:
fFileId(0),
diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.h b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h
index ca41768fd2..efcb6cbf96 100644
--- a/src/add-ons/kernel/file_systems/nfs4/FileInfo.h
+++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h
@@ -16,6 +16,8 @@
#include
#include
+#include "Debug.h"
+
#define NFS4_FHSIZE 128
@@ -32,6 +34,8 @@ struct FileHandle {
inline bool operator!=(const FileHandle& handle) const;
inline bool operator>(const FileHandle& handle) const;
inline bool operator<(const FileHandle& handle) const;
+
+ void Dump(void (*xprintf)(const char*, ...) = dprintf) const;
};
struct InodeNames;
@@ -52,6 +56,10 @@ struct InodeNames : public KernelReferenceable {
bool RemoveName(InodeNames* parent,
const char* name);
+ void Dump(void (*xprintf)(const char*, ...) = dprintf);
+
+ void _DumpLocked(void (*xprintf)(const char*, ...)) const;
+
mutex fLock;
SinglyLinkedList fNames;
FileHandle fHandle;
diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp
index 8f5ed2e375..a79c158c07 100644
--- a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp
@@ -20,17 +20,6 @@
#include "VnodeToInode.h"
-#define ERROR(x...) dprintf("nfs4: " x)
-
-#ifdef DEBUG
-#define TRACE(x...) dprintf("nfs4: " x)
-#define CALLED() dprintf("nfs4: called %s", __func__)
-#else
-#define TRACE(x...)
-#define CALLED()
-#endif
-
-
extern RPC::ServerManager* gRPCServerManager;
extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv);
@@ -522,6 +511,21 @@ FileSystem::EnsureNoCollision(ino_t newID, const FileHandle& handle)
}
+void
+FileSystem::Dump(void (*xprintf)(const char*, ...))
+{
+ MutexLocker locker;
+ if (xprintf != kprintf)
+ locker.SetTo(fOpenLock, false);
+
+ fInoIdMap.Dump(xprintf);
+
+ _DumpLocked(xprintf);
+
+ return;
+}
+
+
status_t
FileSystem::_ParsePath(RequestBuilder& req, uint32& count, const char* _path)
{
@@ -561,3 +565,21 @@ FileSystem::_ParsePath(RequestBuilder& req, uint32& count, const char* _path)
return B_OK;
}
+
+void
+FileSystem::_DumpLocked(void (*xprintf)(const char*, ...)) const
+{
+ xprintf("fOpenFiles:\n", fOpenFiles);
+ for (DoublyLinkedList::ConstIterator it = fOpenFiles.GetIterator();
+ const OpenState* state = it.Next();) {
+ xprintf("\tID\t\t%" B_PRIu64 "\n", state->fInfo.fFileId);
+ xprintf("\tFileHandle\t");
+ state->fInfo.fHandle.Dump(xprintf);
+ xprintf("\tInodeNames\t");
+ state->fInfo.fNames->Dump(xprintf);
+ xprintf("\t----------\n");
+ }
+
+ return;
+}
+
diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h
index bcf13e7a3e..79a4bb04bc 100644
--- a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h
+++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h
@@ -11,6 +11,7 @@
#include
+#include "Debug.h"
#include "Delegation.h"
#include "InodeIdMap.h"
#include "NFS4Defs.h"
@@ -82,12 +83,17 @@ public:
inline mutex& CreateFileLock();
void EnsureNoCollision(ino_t newID, const FileHandle& handle);
+
+ void Dump(void (*xprintf)(const char*, ...) = dprintf);
+
private:
FileSystem(const MountConfiguration& config);
static status_t _ParsePath(RequestBuilder& req, uint32& count,
const char* _path);
+ void _DumpLocked(void (*xprintf)(const char*, ...)) const;
+
mutex fCreateFileLock;
mutex fDelegationLock;
diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp
index f2ca133ab6..a60345c973 100644
--- a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp
@@ -1023,3 +1023,19 @@ Inode::EndAIOOp()
release_sem(fAIOWait);
}
+
+/*! Print the ID, handle, names, and DirectoryCache if applicable.
+ @pre The parent VnodeToInode is locked.
+*/
+void
+Inode::Dump(void (*xprintf)(const char*, ...)) const
+{
+ xprintf("Inode\t%" B_PRIu64 " at %p\n", fInfo.fFileId, this);
+ xprintf("FileHandle\t");
+ fInfo.fHandle.Dump(xprintf);
+ xprintf("InodeNames\t");
+ fInfo.fNames->Dump(xprintf);
+ if (fCache != NULL)
+ fCache->Dump(xprintf);
+}
+
diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h
index 98155231b4..9f7297c2e2 100644
--- a/src/add-ons/kernel/file_systems/nfs4/Inode.h
+++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h
@@ -121,6 +121,9 @@ public:
inline void SetStale(bool stale = true);
inline bool IsStale() const;
+
+ void Dump(void (*xprintf)(const char*, ...) = dprintf) const;
+
protected:
Inode();
diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.cpp
index 7f1ca2c529..5cf9c85119 100644
--- a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.cpp
@@ -79,3 +79,34 @@ InodeIdMap::GetFileInfo(FileInfo* fileInfo, ino_t id)
return B_OK;
}
+
+void
+InodeIdMap::Dump(void (*xprintf)(const char*, ...))
+{
+ MutexLocker locker;
+ if (xprintf != kprintf)
+ locker.SetTo(fLock, false);
+
+ _DumpLocked(xprintf);
+
+ return;
+}
+
+
+void
+InodeIdMap::_DumpLocked(void (*xprintf)(const char*, ...)) const
+{
+ xprintf("InodeIdMap at %p\n", this);
+ xprintf("\tino\thandle\n");
+ AVLTreeMap::ConstIterator iterator = fMap.GetIterator();
+ for (iterator.Next(); iterator.HasNext(); iterator.Next()) {
+ ino_t ino = iterator.CurrentKey();
+ xprintf("\t%" B_PRIdINO "\t", ino);
+ const FileInfo* fileInfo = iterator.CurrentValuePointer();
+ if (fileInfo != NULL)
+ fileInfo->fHandle.Dump(xprintf);
+ }
+
+ return;
+}
+
diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h
index a375c7702d..6465207248 100644
--- a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h
+++ b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h
@@ -31,6 +31,10 @@ public:
status_t RemoveEntry(ino_t id);
status_t GetFileInfo(FileInfo* fileInfo,
ino_t id);
+ void Dump(void (*xprintf)(const char*, ...) = dprintf);
+
+private:
+ void _DumpLocked(void (*xprintf)(const char*, ...)) const;
private:
AVLTreeMap fMap;
diff --git a/src/add-ons/kernel/file_systems/nfs4/Jamfile b/src/add-ons/kernel/file_systems/nfs4/Jamfile
index da8f82eb75..f7df3a6477 100644
--- a/src/add-ons/kernel/file_systems/nfs4/Jamfile
+++ b/src/add-ons/kernel/file_systems/nfs4/Jamfile
@@ -1,11 +1,15 @@
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ;
UsePrivateKernelHeaders ;
-UsePrivateHeaders shared ;
+UsePrivateHeaders file_systems shared ;
+
+DEFINES += DEBUG_APP="\\\"nfs4\\\"" ;
KernelAddon nfs4 :
Cookie.cpp
Connection.cpp
+ Debug.cpp
+ DebugSupport.cpp
Delegation.cpp
DirectoryCache.cpp
FileInfo.cpp
@@ -40,5 +44,8 @@ KernelAddon nfs4 :
XDR.cpp
;
+SEARCH on [ FGristFiles DebugSupport.cpp ]
+ += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems shared ] ;
+
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h
index b51d12ed0f..d0d25142b4 100644
--- a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h
+++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h
@@ -14,10 +14,6 @@
#include
-#ifdef USER
-extern "C" void dprintf(const char *format,...);
-#endif
-
enum Procedure {
ProcNull = 0,
diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp
index ae8b0bcbd7..38430b9ee4 100644
--- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp
@@ -17,17 +17,6 @@
#include "Cookie.h"
-#define ERROR(x...) dprintf("nfs4: " x)
-
-#ifdef DEBUG
-#define TRACE(x...) dprintf("nfs4: " x)
-#define CALLED() dprintf("nfs4: called %s", __func__)
-#else
-#define TRACE(x...)
-#define CALLED()
-#endif
-
-
static status_t
ProcessStream(RPC::Reply* reply, const char* callName)
{
diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp
index 6e260bc4ba..198964017c 100644
--- a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp
@@ -51,3 +51,21 @@ VnodeToInode::Unlink(InodeNames* parent, const char* name)
return false;
}
+
+void
+VnodeToInode::Dump(void (*xprintf)(const char*, ...))
+{
+ xprintf("VTI\t%" B_PRIdINO " at %p\n", fID);
+
+ ReadLocker locker;
+ if (xprintf != kprintf)
+ locker.SetTo(fLock, false);
+
+ if (fInode != NULL)
+ fInode->Dump(xprintf);
+ else
+ xprintf("NULL Inode");
+
+ return;
+}
+
diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h
index ee04a454c3..c008c49809 100644
--- a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h
+++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h
@@ -34,6 +34,8 @@ public:
inline ino_t ID() const;
inline bool IsRoot() const;
+
+ void Dump(void (*xprintf)(const char*, ...) = dprintf);
private:
ino_t fID;
rw_lock fLock;
diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp
index f0d71d47fd..a8c68bed40 100644
--- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp
+++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp
@@ -25,16 +25,6 @@
#include "WorkQueue.h"
-#define ERROR(format, args...) \
- dprintf("nfs4: %s()" format "\n", __func__ , ##args)
-
-#ifdef DEBUG
-#define TRACE(format, args...) \
- dprintf("nfs4: %s()" format "\n", __func__ , ##args)
-#else
-#define TRACE(x...) (void)0
-#endif
-
extern fs_volume_ops gNFSv4VolumeOps;
extern fs_vnode_ops gNFSv4VnodeOps;
@@ -1403,6 +1393,8 @@ nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
status_t
nfs4_init()
{
+ init_debugging();
+
gRPCServerManager = new(std::nothrow) RPC::ServerManager;
if (gRPCServerManager == NULL)
return B_NO_MEMORY;
@@ -1418,6 +1410,11 @@ nfs4_init()
return B_NO_MEMORY;
}
+#ifdef _KERNEL_MODE
+ add_debugger_command("nfs4", kprintf_volume, "dump an nfs4 volume");
+ add_debugger_command("nfs4_inode", kprintf_inode, "dump an nfs4 inode");
+#endif // _KERNEL_MODE
+
return B_OK;
}
@@ -1425,6 +1422,8 @@ nfs4_init()
status_t
nfs4_uninit()
{
+ exit_debugging();
+
RPC::CallbackServer::ShutdownAll();
delete gIdMapper;
@@ -1433,6 +1432,11 @@ nfs4_uninit()
mutex_destroy(&gIdMapperLock);
+#ifdef _KERNEL_MODE
+ remove_debugger_command("nfs4", kprintf_volume);
+ remove_debugger_command("nfs4_inode", kprintf_inode);
+#endif // _KERNEL_MODE
+
return B_OK;
}
diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/nfs4/Jamfile b/src/tests/add-ons/kernel/file_systems/userlandfs/nfs4/Jamfile
index 08aec3479c..b6e1041956 100644
--- a/src/tests/add-ons/kernel/file_systems/userlandfs/nfs4/Jamfile
+++ b/src/tests/add-ons/kernel/file_systems/userlandfs/nfs4/Jamfile
@@ -8,6 +8,8 @@ SEARCH_SOURCE += $(nfs4Top) ;
{
local defines =
USER=1
+ DEBUG_APP="\\\"userlandfs_server\\\""
+ # with this setting, output of the driver will be integrated with that of the server when DEBUG_PRINT is defined for both
;
defines = [ FDefines $(defines) ] ;
@@ -15,12 +17,14 @@ SEARCH_SOURCE += $(nfs4Top) ;
}
UsePrivateKernelHeaders ;
-UsePrivateHeaders shared ;
+UsePrivateHeaders file_systems shared ;
Addon nfs4
:
Cookie.cpp
Connection.cpp
+ Debug.cpp
+ DebugSupport.cpp
Delegation.cpp
DirectoryCache.cpp
FileInfo.cpp
@@ -59,4 +63,7 @@ Addon nfs4
[ TargetLibsupc++ ]
;
+SEARCH on [ FGristFiles DebugSupport.cpp ]
+ += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems shared ] ;
+
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;