Skip to content

testing/libc/arch_libc: Extend coverage to all string/memory functions - #3712

Open
xiaoxiang781216 wants to merge 4 commits into
apache:masterfrom
xiaoxiang781216:upstream-arch-libc
Open

testing/libc/arch_libc: Extend coverage to all string/memory functions#3712
xiaoxiang781216 wants to merge 4 commits into
apache:masterfrom
xiaoxiang781216:upstream-arch-libc

Conversation

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

Summary

apps/testing/libc/arch_libc only exercised strcpy(), so the architecture
optimized implementations of every other string and memory routine were never
covered by the test suite. This series turns it into a full correctness and
speed harness for the whole string/memory family, which is the prerequisite for
landing architecture optimized assembly with confidence.

Four commits:

  1. Add tests for all string/memory functions.
    Adds a correctness test (sweeping buffer alignment and transfer size) and a
    speed test (average cycles via perf_gettime()) for memcpy, memmove,
    memset, memcmp, memchr, strlen, strcmp, strchr, strncmp,
    strnlen, strncpy, stpcpy, strcat and strrchr. Each test is
    selected by its own CONFIG_TESTING_ARCH_LIBC_<FUNC> option (default y) so
    a target can drop the ones it does not need.

  2. Add strchrnul test and sweep size boundaries.
    Adds strchrnul coverage, and sweeps alignment 0..7 together with the
    boundary sizes {0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128,
    129, 255, 256, 257} in the scan function tests and in memmove. Those
    sizes sit on the 8/16-byte chunk edges and on the sub-word tails, so
    vectorized (NEON/MVE) and word-at-a-time implementations are stressed
    exactly at their alignment and size boundaries. memmove is additionally
    exercised across four overlap layouts: forward, backward, contained and
    adjacent.

  3. Fix out-of-bounds write in memmove test.
    The adjacent overlap layout introduced by (2) started at a fixed
    g_buf1 + align + 64, so the destination tail ran past g_buf1 for the
    largest swept sizes (AddressSanitizer reported a global-buffer-overflow).
    Start the layout at g_buf1 + align instead.

  4. Cover unaligned src/dst copy paths.
    strcpy/strncpy/stpcpy applied the same offset to source and
    destination, so the two pointers always shared the same word congruence and
    the byte prologue plus shift-merge path of optimized copy routines was never
    reached. Vary both offsets independently over 0..7. Also drops the
    ARCH_TOOLCHAIN_GNU dependency from TESTING_ARCH_LIBC: the test only uses
    standard C string functions and perf_gettime(), with no GNU specific
    construct, so it builds with non-GNU toolchains such as TASKING as well.

Commit 3 is a fix for commit 2 rather than a squash because the two commits
have different authors; each commit still builds and runs standalone
(see Testing).

Impact

  • Test code only. Nothing is built unless CONFIG_TESTING_ARCH_LIBC
    (default n) is selected, so no existing board configuration or defconfig
    changes, and no size impact on any shipped build.
  • New Kconfig options TESTING_ARCH_LIBC_{MEMCHR,MEMCMP,MEMCPY,MEMMOVE,MEMSET, STRCHR,STRCMP,STRCPY,STRLEN,STRNCMP,STRNLEN,STRNCPY,STPCPY,STRCAT,STRRCHR, STRCHRNUL}, all default y inside TESTING_ARCH_LIBC.
  • Dropping the ARCH_TOOLCHAIN_GNU dependency only widens the set of
    toolchains that may select the test; no existing configuration changes
    behaviour.
  • No user API/ABI, hardware, security or documentation impact.

Testing

Host: Ubuntu 24.04 x86_64, gcc 13.3.0
Target: sim:nsh with CONFIG_TESTING_ARCH_LIBC=y (all 16 function options
enabled) and CONFIG_TESTING_ARCH_LIBC_VERBOSE=y

Every commit of the series was built and run standalone; no build warnings, and
all enabled functions report PASSED:

commit functions PASSED result
1 Add tests for all string/memory functions. 15 arch_libc_test Passed
2 Add strchrnul test and sweep size boundaries. 16 arch_libc_test Passed
3 Fix out-of-bounds write in memmove test. 16 arch_libc_test Passed
4 Cover unaligned src/dst copy paths. 16 arch_libc_test Passed

A build with only CONFIG_TESTING_ARCH_LIBC_STRCPY=y (every other function
test disabled) was also checked to make sure the reduced configurations stay
warning free.

Before (apache/master, only strcpy is covered):

NuttShell (NSH) NuttX-10.4.0
nsh> arch_libctest
arch_libc_test_strcpy Test Passed
strcpy total(run 25 times) cpu cycles 1749
strcpy average cpu cycles 69
nsh> exit

After (this series):

NuttShell (NSH) NuttX-10.4.0
nsh> arch_libctest
Testing memcpy...
memcpy: PASSED
memcpy(128) avg cycles: 96
Testing memmove...
memmove: PASSED
memmove(128) avg cycles: 4
Testing memset...
memset: PASSED
memset(128) avg cycles: 53
Testing memcmp...
memcmp: PASSED
memcmp(128) avg cycles: 121
Testing memchr...
memchr: PASSED
memchr(128) avg cycles: 12
Testing strlen...
strlen: PASSED
strlen(128) avg cycles: 50
Testing strcmp...
strcmp: PASSED
strcmp(128) avg cycles: 133
Testing strcpy...
strcpy: PASSED
strcpy(128) avg cycles: 234
Testing strchr...
strchr: PASSED
strchr(128) avg cycles: 54
Testing strncmp...
strncmp: PASSED
strncmp(128) avg cycles: 469
Testing strnlen...
strnlen: PASSED
strnlen(128) avg cycles: 281
Testing strncpy...
strncpy: PASSED
strncpy(128) avg cycles: 142
Testing stpcpy...
stpcpy: PASSED
stpcpy(128) avg cycles: 68
Testing strcat...
strcat: PASSED
strcat(64) avg cycles: 37
Testing strrchr...
strrchr: PASSED
strrchr(128) avg cycles: 65
Testing strchrnul...
strchrnul: PASSED
strchrnul(128) avg cycles: 18
arch_libc_test Passed
nsh> exit

tools/checkpatch.sh -c -u -m -g apache/master..HEAD reports
All checks pass. for the series.

xiaoxiang781216 and others added 4 commits August 9, 2026 22:33
The arch_libc test only covered strcpy, so the architecture optimized
implementations of the remaining string and memory routines were never
exercised by the test suite.

Extend the test to also cover memcpy, memmove, memset, memcmp, memchr,
strlen, strcmp, strchr, strncmp, strnlen, strncpy, stpcpy, strcat and
strrchr:

* Every function gets a correctness test that sweeps the buffer
  alignment and the transfer size and compares the result against the
  expected value.
* Every function gets a speed test that reports the average cycle count
  measured with perf_gettime().
* Every individual test is selected by its own
  CONFIG_TESTING_ARCH_LIBC_<FUNC> option (default y), so a target can
  drop the ones it does not need.

Impact: test only.  Nothing is built unless CONFIG_TESTING_ARCH_LIBC
(default n) is selected, so no existing board configuration changes.

Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y.  All 15 enabled functions
report PASSED and "arch_libc_test Passed".

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Add test_strchrnul() and speed_strchrnul(), selected by the new
CONFIG_TESTING_ARCH_LIBC_STRCHRNUL option, covering the hit, miss and
NUL cases.

Sweep alignment 0..7 and the boundary sizes {0, 1, 7, 8, 9, 15, 16, 17,
31, 32, 33, 63, 64, 65, 127, 128, 129, 255, 256, 257} in the scan
function tests (memcmp, memchr, strlen, strcmp, strchr, strncmp,
strnlen, strrchr) and in memmove.  Those sizes sit on the 8 and 16 byte
chunk edges and on the sub-word tails, so vectorized (NEON/MVE) and
word-at-a-time implementations are stressed exactly at their alignment
and size boundaries instead of only at "nice" lengths.  memmove is
additionally exercised across four overlap layouts: forward, backward,
contained and adjacent.

Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).

Testing: built and ran qemu-armv7a:nsh (Cortex-A7, generic C
implementation) and sim:nsh on Linux x86_64 (Ubuntu 24.04, gcc 13.3.0)
with CONFIG_TESTING_ARCH_LIBC=y.  All 16 enabled functions report
PASSED and "arch_libc_test Passed".  These tests pass against the
generic C routines, which establishes the correctness baseline before
architecture optimized assembly is introduced.

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
The adjacent overlap case in test_memmove() placed the source at a fixed
g_buf1 + align + 64 and the destination one size further, so the
destination tail reached align + 64 + 2 * size.  g_buf1 is only
TEST_BUF_SIZE + MAX_ALIGN (528) bytes, so the larger swept sizes ran off
the end: align=0 with size=255 writes up to offset 573, that is 46 bytes
past the object.  AddressSanitizer aborted arch_libctest with a
global-buffer-overflow.

Start the adjacent layout at g_buf1 + align instead.  The tail then
reaches align + 2 * size, which is at most 7 + 2 * 257 = 521 and stays
inside g_buf1 for every alignment and boundary size that is swept, while
still keeping source and destination exactly adjacent.

Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).

Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y.  memmove reports PASSED
with no sanitizer report, and "arch_libc_test Passed".

Assisted-by: Claude:claude-opus-5
Signed-off-by: dengwenqi <dengwenqi@xiaomi.com>
test_strcpy(), test_strncpy() and test_stpcpy() applied the same offset
to the source and to the destination, so both pointers always shared the
same word congruence.  Architecture optimized copy routines take a
different code path when the two offsets differ: a byte prologue to
align the destination, then either a byte fallback or a shift-merge loop
that recombines two source words per store.  None of that was reached by
the test.

Vary the source and destination offsets independently over 0..7 in those
three tests, so both the equal congruence (aligned word copy) and the
unequal congruence (shift-merge) paths are covered, and report both
offsets on failure so a regression points at the offending combination.

Also drop the ARCH_TOOLCHAIN_GNU dependency from TESTING_ARCH_LIBC.  The
test only uses standard C string functions and perf_gettime(), with no
GNU specific construct, so it builds with non GNU toolchains such as
TASKING as well.

Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).
Dropping the ARCH_TOOLCHAIN_GNU dependency only widens the set of
toolchains that may select the test, no existing configuration changes.

Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y.  strcpy, strncpy and
stpcpy report PASSED for all 64 offset combinations, and
"arch_libc_test Passed".

Assisted-by: Claude:claude-opus-5
Signed-off-by: zhangyuan29 <zhangyuan29@xiaomi.com>

@cederom cederom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @xiaoxiang781216 :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants