Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/components/filesystem/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ NuttX provides support for a variety of file systems out of the box.
nxffs.rst
partition.rst
procfs.rst
profiler.rst
romfs.rst
rpmsgfs.rst
smartfs.rst
Expand Down
38 changes: 38 additions & 0 deletions Documentation/components/filesystem/profiler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
==============================
VFS Performance Profiler
==============================

The Virtual File System (VFS) Performance Profiler provides a simple, in-kernel
mechanism to track execution times and invocation counts for core VFS operations
(read, write, open, close) seamlessly. This is highly suitable for
CI/CD automated regression testing and performance bottleneck identification.

Configuration
=============

To enable the profiler, select ``CONFIG_FS_PROFILER`` in your Kconfig.
To expose the metrics dynamically via procfs, ensure ``CONFIG_FS_PROCFS`` is enabled, and
the profiler node is included via ``CONFIG_FS_PROCFS_PROFILER``.

Usage
=====

When enabled, the profiler automatically intercepts calls to the underlying
inode operations and records the execution elapsed times using ``perf_gettime()``.
Since no blocking mutexes are used during updates (fast ``atomic.h`` operations
are utilized instead), the overhead is extremely minimal and safely scales on SMP.

To view the current statistics collectively from the NuttShell (NSH), simply
read the node:

.. code-block:: bash
nsh> cat /proc/fs/profile
VFS Performance Profile:
Reads: 12 (Total time: 4500120 ns)
Writes: 3 (Total time: 95050 ns)
Opens: 15 (Total time: 1005000 ns)
Closes: 15 (Total time: 45000 ns)
The reported times are in the raw ticks/units provided by ``perf_gettime()`` on
your specific architecture.
8 changes: 8 additions & 0 deletions fs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

comment "File system configuration"

config FS_PROFILER
bool "VFS Performance Profiler"
default n
---help---
Enable nanosecond/microsecond-level profiling for the Virtual File
System (VFS) operations (open, close, read, write). The profile stats
can be read via /proc/fs/profile if PROCFS is enabled.

config DISABLE_MOUNTPOINT
bool "Disable support for mount points"
default n
Expand Down
4 changes: 4 additions & 0 deletions fs/procfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ if(NOT CONFIG_DISABLE_MOUNTPOINT)
list(APPEND SRCS fs_procfspressure.c)
endif()

if(CONFIG_FS_PROCFS_PROFILER)
list(APPEND SRCS fs_procfsprofile.c)
endif()

target_sources(fs PRIVATE ${SRCS})

endif()
Expand Down
10 changes: 10 additions & 0 deletions fs/procfs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ config FS_PROCFS_EXCLUDE_VERSION
bool "Exclude version"
default DEFAULT_SMALL

config FS_PROCFS_PROFILER
bool "Include fs/profile information"
depends on FS_PROFILER && !DEFAULT_SMALL
default n
---help---
Enable the VFS performance profiler procfs node at /proc/fs/profile.
This node provides real-time dynamic statistics (call counts, elapsed
time) for core VFS operations (read, write, open, close) to help
identify filesystem performance bottlenecks and regressions.

config FS_PROCFS_INCLUDE_PRESSURE
bool "Include memory pressure notification"
default n
Expand Down
4 changes: 4 additions & 0 deletions fs/procfs/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ ifeq ($(CONFIG_FS_PROCFS_INCLUDE_PRESSURE),y)
CSRCS += fs_procfspressure.c
endif

ifeq ($(CONFIG_FS_PROCFS_PROFILER),y)
CSRCS += fs_procfsprofile.c
endif

# Include procfs build support

DEPPATH += --dep-path procfs
Expand Down
6 changes: 6 additions & 0 deletions fs/procfs/fs_procfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ extern const struct procfs_operations g_thermal_operations;
extern const struct procfs_operations g_uptime_operations;
extern const struct procfs_operations g_version_operations;
extern const struct procfs_operations g_pressure_operations;
#if defined(CONFIG_FS_PROFILER) && defined(CONFIG_FS_PROCFS_PROFILER)
extern const struct procfs_operations g_vfs_profile_operations;
#endif

/* This is not good. These are implemented in other sub-systems. Having to
* deal with them here is not a good coupling. What is really needed is a
Expand Down Expand Up @@ -208,6 +211,9 @@ static const struct procfs_entry_s g_procfs_entries[] =
#ifndef CONFIG_FS_PROCFS_EXCLUDE_VERSION
{ "version", &g_version_operations, PROCFS_FILE_TYPE },
#endif
#if defined(CONFIG_FS_PROFILER) && defined(CONFIG_FS_PROCFS_PROFILER)
{ "profile", &g_vfs_profile_operations, PROCFS_FILE_TYPE },
#endif
};

#ifdef CONFIG_FS_PROCFS_REGISTER
Expand Down
126 changes: 126 additions & 0 deletions fs/procfs/fs_procfsprofile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/****************************************************************************
* fs/procfs/fs_procfsprofile.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/

/****************************************************************************
* Included Files
****************************************************************************/

#include <nuttx/config.h>

#if defined(CONFIG_FS_PROCFS) && defined(CONFIG_FS_PROFILER) && \
defined(CONFIG_FS_PROCFS_PROFILER)

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

#include <nuttx/fs/fs.h>
#include <nuttx/fs/procfs.h>
#include "../vfs/vfs.h"

/****************************************************************************
* Private Functions
****************************************************************************/

static int profile_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
return OK;
}

static int profile_close(FAR struct file *filep)
{
return OK;
}

static ssize_t profile_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
char buf[256];
size_t linesize;
off_t offset = filep->f_pos;

if (offset > 0)
{
return 0;
}

procfs_snprintf(buf, sizeof(buf),
"VFS Performance Profile:\n"
" Reads: %10lu (Total time: %llu ns)\n"
" Writes: %10lu (Total time: %llu ns)\n"
" Opens: %10lu (Total time: %llu ns)\n"
" Closes: %10lu (Total time: %llu ns)\n",
(unsigned long)g_vfs_profile.reads,
(unsigned long long)g_vfs_profile.total_read_time,
(unsigned long)g_vfs_profile.writes,
(unsigned long long)g_vfs_profile.total_write_time,
(unsigned long)g_vfs_profile.opens,
(unsigned long long)g_vfs_profile.total_open_time,
(unsigned long)g_vfs_profile.closes,
(unsigned long long)g_vfs_profile.total_close_time);

linesize = strlen(buf);
if (linesize > buflen)
{
linesize = buflen;
}

memcpy(buffer, buf, linesize);
filep->f_pos += linesize;
return linesize;
}

static int profile_dup(FAR const struct file *oldp, FAR struct file *newp)
{
return OK;
}

static int profile_stat(FAR const char *relpath, FAR struct stat *buf)
{
memset(buf, 0, sizeof(struct stat));
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
return OK;
}

/****************************************************************************
* Public Data
****************************************************************************/

const struct procfs_operations g_vfs_profile_operations =
{
profile_open, /* open */
profile_close, /* close */
profile_read, /* read */
NULL, /* write */
NULL, /* poll */
profile_dup, /* dup */
NULL, /* opendir */
NULL, /* closedir */
NULL, /* readdir */
NULL, /* rewinddir */
profile_stat /* stat */
};

#endif

6 changes: 6 additions & 0 deletions fs/vfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,10 @@ if(CONFIG_SIGNAL_FD)
list(APPEND SRCS fs_signalfd.c)
endif()

# Support for profiler

if(CONFIG_FS_PROFILER)
list(APPEND SRCS fs_profile.c)
endif()

target_sources(fs PRIVATE ${SRCS})
4 changes: 4 additions & 0 deletions fs/vfs/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ ifeq ($(CONFIG_SIGNAL_FD),y)
CSRCS += fs_signalfd.c
endif

ifeq ($(CONFIG_FS_PROFILER),y)
CSRCS += fs_profile.c
endif

# Include vfs build support

DEPPATH += --dep-path vfs
Expand Down
7 changes: 7 additions & 0 deletions fs/vfs/fs_close.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,16 @@ int file_close(FAR struct file *filep)

if (inode->u.i_ops && inode->u.i_ops->close)
{
clock_t start_time;

VFS_PROFILE_START(start_time);

/* Perform the close operation */

ret = inode->u.i_ops->close(filep);

VFS_PROFILE_STOP(start_time, g_vfs_profile.total_close_time,
g_vfs_profile.closes);
}

/* And release the inode */
Expand Down
7 changes: 7 additions & 0 deletions fs/vfs/fs_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ static int file_vopen(FAR struct file *filep, FAR const char *path,
* because it may also be closed that many times.
*/

clock_t start_time;

VFS_PROFILE_START(start_time);

if (oflags & O_DIRECTORY)
{
ret = dir_allocate(filep, desc.relpath);
Expand All @@ -261,6 +265,9 @@ static int file_vopen(FAR struct file *filep, FAR const char *path,
ret = -ENXIO;
}

VFS_PROFILE_STOP(start_time, g_vfs_profile.total_open_time,
g_vfs_profile.opens);

if (ret == -EISDIR && ((oflags & O_WRONLY) == 0))
{
ret = dir_allocate(filep, desc.relpath);
Expand Down
55 changes: 55 additions & 0 deletions fs/vfs/fs_profile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/****************************************************************************
* fs/vfs/fs_profile.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/

/****************************************************************************
* Included Files
****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/clock.h>
#include <nuttx/atomic.h>
#include "vfs.h"

/****************************************************************************
* Public Data
****************************************************************************/

struct vfs_profile_s g_vfs_profile;

/****************************************************************************
* Public Functions
****************************************************************************/

void vfs_profile_start(FAR clock_t *start)
{
*start = perf_gettime();
}

void vfs_profile_stop(FAR clock_t *start, FAR uint64_t *total,
FAR uint32_t *count)
{
clock_t stop = perf_gettime();
clock_t delta = stop - *start;

atomic64_fetch_add((FAR atomic64_t *)total, delta);
atomic_fetch_add((FAR atomic_t *)count, 1);
}
7 changes: 7 additions & 0 deletions fs/vfs/fs_read.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ ssize_t file_readv(FAR struct file *filep,

else if (inode != NULL && inode->u.i_ops)
{
clock_t start_time;

VFS_PROFILE_START(start_time);

if (inode->u.i_ops->readv)
{
struct uio uio;
Expand All @@ -220,6 +224,9 @@ ssize_t file_readv(FAR struct file *filep,
{
ret = file_readv_compat(filep, iov, iovcnt);
}

VFS_PROFILE_STOP(start_time, g_vfs_profile.total_read_time,
g_vfs_profile.reads);
}

/* Return the number of bytes read (or possibly an error code) */
Expand Down
Loading
Loading