From 221e8a14e2b5e84ca7146f65c15f0bff0d127fe2 Mon Sep 17 00:00:00 2001 From: navidpadid Date: Sat, 20 Dec 2025 03:19:24 +0000 Subject: [PATCH 1/3] initial unit tests added --- Makefile | 13 ++++++++++++- README.md | 37 ++++++++++++++++++++++++++++++++----- src/elf_det.c | 14 +++----------- src/lib/elf_helpers.h | 32 ++++++++++++++++++++++++++++++++ src/lib/user_helpers.h | 18 ++++++++++++++++++ src/proc_elf_ctrl.c | 21 +++++++++++++++++---- tests/elf_helpers_test.c | 23 +++++++++++++++++++++++ tests/test_elf_helpers.c | 23 +++++++++++++++++++++++ tests/test_user_helpers.c | 21 +++++++++++++++++++++ tests/user_helpers_test.c | 21 +++++++++++++++++++++ 10 files changed, 202 insertions(+), 21 deletions(-) create mode 100644 src/lib/elf_helpers.h create mode 100644 src/lib/user_helpers.h create mode 100644 tests/elf_helpers_test.c create mode 100644 tests/test_elf_helpers.c create mode 100644 tests/test_user_helpers.c create mode 100644 tests/user_helpers_test.c diff --git a/Makefile b/Makefile index 63c8947..0c754f3 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ SRC_DIR := src # Build directory for user program BUILD_DIR := build -.PHONY: all clean module user install uninstall test help +.PHONY: all clean module user install uninstall test help unit # Default target all: module user @@ -38,6 +38,17 @@ user: gcc -Wall -o $(BUILD_DIR)/$(USER_PROG) $(SRC_DIR)/$(USER_PROG).c @echo "User program built successfully!" +# Function-level unit tests (user-space) +unit: + @echo "Building function-level unit tests..." + @mkdir -p $(BUILD_DIR) + gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/elf_helpers_test tests/elf_helpers_test.c + gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/user_helpers_test tests/user_helpers_test.c + @echo "Running unit tests..." + @$(BUILD_DIR)/elf_helpers_test + @$(BUILD_DIR)/user_helpers_test + @echo "All function-level unit tests passed!" + # Install kernel module (requires root) install: module @echo "Installing kernel module..." diff --git a/README.md b/README.md index 195d08c..9fb6e53 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ See [scripts/README.md](scripts/README.md) for detailed QEMU testing documentati | `make install` | Install kernel module (requires root) | | `make uninstall` | Remove kernel module (requires root) | | `make test` | Install module and run user program | +| `make unit` | Build and run function-level unit tests (no kernel required) | | `make clean` | Remove all build artifacts | | `make help` | Display help message | @@ -213,7 +214,7 @@ The kernel module creates entries in `/proc/elf_det/`: - `/proc/elf_det/det` - Read-only file to retrieve process information **Key Functions:** -- `tops_show()` - Main function to gather and format process information +- `elfdet_show()` - Main function to gather and format process information - `procfile_write()` - Handles PID input from user space - `procfile_read()` - Returns formatted process data @@ -225,10 +226,23 @@ The kernel module creates entries in `/proc/elf_det/`: ### User Program (`proc_elf_ctrl.c`) -Simple C program that: -1. Prompts user for a PID -2. Writes PID to `/proc/elf_det/pid` -3. Reads and displays process information from `/proc/elf_det/det` +Simple C program that supports two modes: +1. Interactive: prompts for a PID, writes to `/proc/elf_det/pid`, then reads `/proc/elf_det/det` and prints two lines +2. Argument mode: run `./build/proc_elf_ctrl ` to write the PID and print exactly two lines non-interactively + +You can override the proc directory for testing with the environment variable `ELF_DET_PROC_DIR`. + +Example: + +```bash +ELF_DET_PROC_DIR=/tmp/fakeproc ./build/proc_elf_ctrl 12345 +``` + +Internally, path construction is handled via helper `build_proc_path()`. + +Helper headers used: +- `src/lib/user_helpers.h` – path building with env override +- `src/lib/elf_helpers.h` – pure functions for CPU usage and BSS range ## Testing @@ -243,6 +257,19 @@ The module has been tested on: - The code has been updated to use modern kernel APIs including VMA iterators and proc_ops **Safe Testing Options:** +### Function-Level Unit Tests + +Run pure function tests (no kernel required): + +```bash +make unit +``` + +This builds and runs: +- `tests/elf_helpers_test.c` – verifies `compute_usage_permyriad()` and `compute_bss_range()` +- `tests/user_helpers_test.c` – verifies `build_proc_path()` with and without `ELF_DET_PROC_DIR` + +Artifacts are created under `build/`. 1. **Dev Container** (current setup) - Isolated from host kernel 2. **QEMU VM** (recommended for extra safety) - See [scripts/README.md](scripts/README.md) 3. **Cloud VM** - Disposable testing environment diff --git a/src/elf_det.c b/src/elf_det.c index 5e3dfbe..d354b04 100644 --- a/src/elf_det.c +++ b/src/elf_det.c @@ -13,6 +13,7 @@ #include //for string libs #include //for task iteration #include //for task_cputime +#include "lib/elf_helpers.h" MODULE_LICENSE("Dual BSD/GPL"); //module license @@ -51,10 +52,7 @@ static int elfdet_show(struct seq_file *m, void *v) { /* CPU usage: total CPU time of task since start divided by elapsed wall time */ total_ns = (u64)task->utime + (u64)task->stime; delta_ns = ktime_get_ns() - task->start_time; - if (delta_ns > 0) - usage_permyriad = (10000ULL * total_ns) / delta_ns; /* percent with two decimals */ - else - usage_permyriad = 0; + usage_permyriad = compute_usage_permyriad(total_ns, delta_ns); // Access VMA using VMA iterator for kernel 6.8+ if (mmap_read_lock_killable(task->mm)) { @@ -64,13 +62,7 @@ static int elfdet_show(struct seq_file *m, void *v) { /* Use mm fields directly for ELF and BSS */ elf_header = task->mm->start_code; - bss_start = task->mm->end_data; - bss_end = task->mm->start_brk; - if (bss_end < bss_start) { - /* If values look wrong due to unusual layout, clear them */ - bss_start = 0; - bss_end = 0; - } + compute_bss_range(task->mm->end_data, task->mm->start_brk, &bss_start, &bss_end); mmap_read_unlock(task->mm); diff --git a/src/lib/elf_helpers.h b/src/lib/elf_helpers.h new file mode 100644 index 0000000..2cdbd57 --- /dev/null +++ b/src/lib/elf_helpers.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __KERNEL__ +#include +typedef u64 eh_u64; +#else +#include +typedef uint64_t eh_u64; +#endif + +/* Compute CPU usage permyriad (percent * 100) from total_ns and delta_ns */ +static inline eh_u64 compute_usage_permyriad(eh_u64 total_ns, eh_u64 delta_ns) +{ + if (delta_ns == 0) return 0; + return (10000ULL * total_ns) / delta_ns; +} + +/* Compute BSS range from end_data and start_brk; returns 0 on invalid */ +static inline int compute_bss_range(unsigned long end_data, + unsigned long start_brk, + unsigned long *out_start, + unsigned long *out_end) +{ + if (start_brk < end_data) { + *out_start = 0; + *out_end = 0; + return 0; + } + *out_start = end_data; + *out_end = start_brk; + return 1; +} diff --git a/src/lib/user_helpers.h b/src/lib/user_helpers.h new file mode 100644 index 0000000..43c37ac --- /dev/null +++ b/src/lib/user_helpers.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +/* Build path to /proc/elf_det/ with optional override via env. */ +static inline char *build_proc_path(const char *name) +{ + const char *base = getenv("ELF_DET_PROC_DIR"); + if (!base || !*base) base = "/proc/elf_det"; + + size_t len = strlen(base) + 1 + strlen(name) + 1; + char *p = (char *)malloc(len); + if (!p) return NULL; + snprintf(p, len, "%s/%s", base, name); + return p; +} diff --git a/src/proc_elf_ctrl.c b/src/proc_elf_ctrl.c index 44f385a..a1ac38f 100644 --- a/src/proc_elf_ctrl.c +++ b/src/proc_elf_ctrl.c @@ -2,6 +2,7 @@ #include #include #include +#include "lib/user_helpers.h" int main(int argc, char **argv) { @@ -13,17 +14,22 @@ int main(int argc, char **argv) strncpy(pid_user, argv[1], sizeof(pid_user) - 1); pid_user[sizeof(pid_user) - 1] = '\0'; - fp = fopen("/proc/elf_det/pid","w"); + char *pid_path = build_proc_path("pid"); + fp = fopen(pid_path, "w"); if (!fp) { perror("open pid"); + free(pid_path); return 1; } fprintf(fp,"%s", pid_user); fclose(fp); + free(pid_path); - fp = fopen("/proc/elf_det/det","r"); + char *det_path = build_proc_path("det"); + fp = fopen(det_path, "r"); if (!fp) { perror("open det"); + free(det_path); return 1; } if (fgets(buff, sizeof(buff), fp)) @@ -31,6 +37,7 @@ int main(int argc, char **argv) if (fgets(buff, sizeof(buff), fp)) printf("%s\n", buff); fclose(fp); + free(det_path); return 0; } @@ -45,18 +52,23 @@ int main(int argc, char **argv) break; } - fp = fopen("/proc/elf_det/pid","w"); + char *pid_path2 = build_proc_path("pid"); + fp = fopen(pid_path2, "w"); if (!fp) { perror("open pid"); + free(pid_path2); return 1; } fprintf(fp,"%s", pid_user); fclose(fp); + free(pid_path2); printf("the process info is here:\n"); - fp = fopen("/proc/elf_det/det","r"); + char *det_path2 = build_proc_path("det"); + fp = fopen(det_path2, "r"); if (!fp) { perror("open det"); + free(det_path2); return 1; } if (fgets(buff, sizeof(buff), fp)) @@ -64,6 +76,7 @@ int main(int argc, char **argv) if (fgets(buff, sizeof(buff), fp)) printf("%s\n", buff); fclose(fp); + free(det_path2); } return 0; } \ No newline at end of file diff --git a/tests/elf_helpers_test.c b/tests/elf_helpers_test.c new file mode 100644 index 0000000..96e63db --- /dev/null +++ b/tests/elf_helpers_test.c @@ -0,0 +1,23 @@ +#include "../src/lib/elf_helpers.h" +#include +#include + +int main(void) { + /* compute_usage_permyriad tests */ + assert(compute_usage_permyriad(0, 1000000ULL) == 0); + assert(compute_usage_permyriad(500000ULL, 1000000ULL) == 5000ULL); + assert(compute_usage_permyriad(250000ULL, 1000000ULL) == 2500ULL); + assert(compute_usage_permyriad(1000000ULL, 1000000ULL) == 10000ULL); + assert(compute_usage_permyriad(0, 0) == 0); + + /* compute_bss_range tests */ + unsigned long s = 0, e = 0; + assert(compute_bss_range(1000UL, 2000UL, &s, &e) == 1); + assert(s == 1000UL && e == 2000UL); + + assert(compute_bss_range(3000UL, 2000UL, &s, &e) == 0); + assert(s == 0UL && e == 0UL); + + puts("elf_helpers tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_elf_helpers.c b/tests/test_elf_helpers.c new file mode 100644 index 0000000..6ab6659 --- /dev/null +++ b/tests/test_elf_helpers.c @@ -0,0 +1,23 @@ +#include "../src/lib/elf_helpers.h" +#include +#include + +int main(void) { + /* compute_usage_permyriad tests */ + assert(compute_usage_permyriad(0, 1000000ULL) == 0); + assert(compute_usage_permyriad(500000ULL, 1000000ULL) == 5000ULL); + assert(compute_usage_permyriad(250000ULL, 1000000ULL) == 2500ULL); + assert(compute_usage_permyriad(1000000ULL, 1000000ULL) == 10000ULL); + assert(compute_usage_permyriad(0, 0) == 0); + + /* compute_bss_range tests */ + unsigned long s = 0, e = 0; + assert(compute_bss_range(1000UL, 2000UL, &s, &e) == 1); + assert(s == 1000UL && e == 2000UL); + + assert(compute_bss_range(3000UL, 2000UL, &s, &e) == 0); + assert(s == 0UL && e == 0UL); + + puts("elf_helpers tests passed"); + return 0; +} diff --git a/tests/test_user_helpers.c b/tests/test_user_helpers.c new file mode 100644 index 0000000..4a2efa0 --- /dev/null +++ b/tests/test_user_helpers.c @@ -0,0 +1,21 @@ +#include "../src/lib/user_helpers.h" +#include +#include +#include + +int main(void) { + /* Without env override */ + unsetenv("ELF_DET_PROC_DIR"); + char *p1 = build_proc_path("pid"); + assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); + free(p1); + + /* With env override */ + setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); + char *p2 = build_proc_path("det"); + assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); + free(p2); + + puts("user_helpers tests passed"); + return 0; +} diff --git a/tests/user_helpers_test.c b/tests/user_helpers_test.c new file mode 100644 index 0000000..0a066fa --- /dev/null +++ b/tests/user_helpers_test.c @@ -0,0 +1,21 @@ +#include "../src/lib/user_helpers.h" +#include +#include +#include + +int main(void) { + /* Without env override */ + unsetenv("ELF_DET_PROC_DIR"); + char *p1 = build_proc_path("pid"); + assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); + free(p1); + + /* With env override */ + setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); + char *p2 = build_proc_path("det"); + assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); + free(p2); + + puts("user_helpers tests passed"); + return 0; +} \ No newline at end of file From d5e15bab896a1ed99b3e99c82b6a2a57f0096f73 Mon Sep 17 00:00:00 2001 From: navidpadid Date: Sun, 4 Jan 2026 04:14:26 +0000 Subject: [PATCH 2/3] tests added and refactored --- LICENSE | 63 ++++++++++++ Makefile | 8 +- README.md | 46 ++++----- scripts/README.md | 64 ++++++++++--- scripts/quick-reference.sh | 95 +++++++++++++++---- .../elf_helpers_test.c => src/elf_det_tests.c | 0 .../proc_elf_ctrl_tests.c | 0 tests/test_elf_helpers.c | 23 ----- tests/test_user_helpers.c | 21 ---- 9 files changed, 213 insertions(+), 107 deletions(-) create mode 100644 LICENSE rename tests/elf_helpers_test.c => src/elf_det_tests.c (100%) rename tests/user_helpers_test.c => src/proc_elf_ctrl_tests.c (100%) delete mode 100644 tests/test_elf_helpers.c delete mode 100644 tests/test_user_helpers.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f23c789 --- /dev/null +++ b/LICENSE @@ -0,0 +1,63 @@ +Dual BSD/GPL License + +This project is licensed under a dual license model: +- You may use this software under the BSD License, OR +- You may use this software under the GNU General Public License version 2 (GPLv2) + +You may choose which license you prefer to follow. + +================================================================================ +BSD License +================================================================================ + +Copyright (c) 2026, Navid Malek +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +GNU General Public License version 2 +================================================================================ + +Copyright (C) 2026 Navid Malek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +================================================================================ + +The full text of the GNU General Public License version 2 can be found at: +https://www.gnu.org/licenses/old-licenses/gpl-2.0.html diff --git a/Makefile b/Makefile index 0c754f3..0677ccb 100644 --- a/Makefile +++ b/Makefile @@ -42,11 +42,11 @@ user: unit: @echo "Building function-level unit tests..." @mkdir -p $(BUILD_DIR) - gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/elf_helpers_test tests/elf_helpers_test.c - gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/user_helpers_test tests/user_helpers_test.c + gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/elf_det_tests $(SRC_DIR)/elf_det_tests.c + gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/proc_elf_ctrl_tests $(SRC_DIR)/proc_elf_ctrl_tests.c @echo "Running unit tests..." - @$(BUILD_DIR)/elf_helpers_test - @$(BUILD_DIR)/user_helpers_test + @$(BUILD_DIR)/elf_det_tests + @$(BUILD_DIR)/proc_elf_ctrl_tests @echo "All function-level unit tests passed!" # Install kernel module (requires root) diff --git a/README.md b/README.md index 9fb6e53..af6b037 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ kernel_module/ ├── src/ │ ├── elf_det.c # Kernel module source code │ ├── proc_elf_ctrl.c # User-space controller program +│ ├── elf_det_tests.c # Unit tests for elf_det functions +│ ├── proc_elf_ctrl_tests.c # Unit tests for proc_elf_ctrl helpers │ └── Kbuild # Kernel build configuration ├── Makefile # Build system ├── .gitignore # Git ignore rules @@ -55,7 +57,9 @@ kernel_module/ Generated after build: └── build/ # Build artifacts (created by make) ├── elf_det.ko # Compiled kernel module - └── proc_elf_ctrl # Compiled user program + ├── proc_elf_ctrl # Compiled user program + ├── elf_det_tests # Compiled unit tests for elf_det + └── proc_elf_ctrl_tests # Compiled unit tests for proc_elf_ctrl ``` ## Prerequisites @@ -168,18 +172,6 @@ For maximum safety, test the kernel module in an isolated QEMU virtual machine t ./scripts/qemu-test.sh ``` -### Requirements - -```bash -# Ubuntu/Debian -sudo apt-get install qemu-system-x86 qemu-utils cloud-image-utils - -# macOS -brew install qemu - -# Fedora -sudo dnf install qemu-system-x86 qemu-img cloud-utils -``` ### What QEMU Testing Does @@ -266,21 +258,14 @@ make unit ``` This builds and runs: -- `tests/elf_helpers_test.c` – verifies `compute_usage_permyriad()` and `compute_bss_range()` -- `tests/user_helpers_test.c` – verifies `build_proc_path()` with and without `ELF_DET_PROC_DIR` +- `src/elf_det_tests.c` – verifies `compute_usage_permyriad()` and `compute_bss_range()` +- `src/proc_elf_ctrl_tests.c` – verifies `build_proc_path()` with and without `ELF_DET_PROC_DIR` Artifacts are created under `build/`. -1. **Dev Container** (current setup) - Isolated from host kernel -2. **QEMU VM** (recommended for extra safety) - See [scripts/README.md](scripts/README.md) +1. **Dev Container** (current setup) - Isolated from host +2. **QEMU VM** (recommended for extra safety from kernel) - See [scripts/README.md](scripts/README.md) 3. **Cloud VM** - Disposable testing environment -## Related Documentation - -For detailed implementation guides, refer to the blog posts: - -- **Part 2.1**: [Implementation of Simple "hello world" Module and Run](http://navidmalek.blog.ir/1396/07/07/Linux-2-1-Implementation-of-Simple-%E2%80%9Chello-world%E2%80%9D-Module-and-Run) -- **Part 2.2**: [Finding specified information and necessary functions](http://navidmalek.blog.ir/1396/07/07/Linux-2-2-Part-2-2-Finding-specified-information-and-necessary-functions-to-implement-desired-module) -- **Part 2.3**: [Implementation of kernel module and user program](http://navidmalek.blog.ir/1396/07/07/Linux-2-3-Part-2-3-Implementation-of-desired-kernel-module-and-user-program) ## Important Notes @@ -329,14 +314,11 @@ This project is licensed under **Dual BSD/GPL** license. ## Author -**Navid Malek** - -- Blog: [navidmalek.blog.ir](http://navidmalek.blog.ir) -- Project: [process-info-kernel-module](https://github.com/navidpadid/process-info-kernel-module) +**Navid Malekghaini** ## Contributing -Contributions, issues, and feature requests are welcome! Feel free to check the issues page. +Contributions, issues, and feature requests are welcome! ## Changelog @@ -344,8 +326,12 @@ Contributions, issues, and feature requests are welcome! Feel free to check the - Initial release with basic process information extraction - Support for CPU usage, memory layout, and ELF sections - User-space controller program -- Dev container support +- Function-level unit tests for core functionality +- Dev container support for isolated development - CI/CD pipeline with GitHub Actions +- QEMU testing environment for safe kernel module testing +- Comprehensive documentation and quick reference guides +- Dual BSD/GPL license --- diff --git a/scripts/README.md b/scripts/README.md index 8ecb7a7..08ddc4b 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,6 +1,10 @@ # QEMU Testing Scripts -Scripts for safely testing the kernel module in an isolated QEMU virtual machine. +Scripts for safely testing the Linux Process Information Kernel Module in an isolated QEMU virtual machine. + +## Overview + +These scripts provide a complete QEMU-based testing environment that isolates kernel module testing from your host system. This is the recommended approach for testing kernel modules as it prevents potential system crashes or instability from affecting your development machine. ## Quick Start @@ -11,7 +15,7 @@ Scripts for safely testing the kernel module in an isolated QEMU virtual machine # 2. Start the QEMU VM ./scripts/qemu-run.sh -# 3. In another terminal, test the module +# 3. In another terminal, run automated tests ./scripts/qemu-test.sh ``` @@ -40,15 +44,21 @@ Scripts for safely testing the kernel module in an isolated QEMU virtual machine - Exit QEMU: Press `Ctrl+A` then `X` ### `qemu-test.sh` -- Automated testing script (run from host) -- Builds module locally +- Automated end-to-end testing script (run from host) +- Builds kernel module and user program locally - Copies files to VM via SCP -- Installs, tests, and uninstalls module +- Installs module, runs tests, and uninstalls cleanly - Shows kernel logs and test results +- Verifies module functionality in isolated environment **Prerequisites:** - VM must be running (`qemu-run.sh`) -- SSH must be accessible +- SSH must be accessible on port 2222 + +### `quick-reference.sh` +- Quick reference guide for common QEMU commands +- Display usage: `./scripts/quick-reference.sh` +- Includes setup, testing, and troubleshooting commands ## Manual Testing @@ -67,7 +77,13 @@ ssh -p 2222 ubuntu@localhost # Inside VM cd ~/ make clean && make all + +# Run unit tests (no kernel required) +make unit + +# Test kernel module sudo make install +lsmod | grep elf_det ./build/proc_elf_ctrl sudo make uninstall ``` @@ -92,12 +108,22 @@ sudo make uninstall ## Why QEMU Testing? -- Complete isolation from host kernel -- Safe crash recovery (just restart VM) -- No risk to host system stability -- Test on exact kernel version -- Easy to reset to clean state -- Reproducible testing environment +Testing kernel modules in QEMU provides critical safety benefits: + +- **Complete Isolation**: Host kernel remains untouched +- **Safe Crash Recovery**: Simply restart the VM if module crashes +- **No Risk to Host**: System instability won't affect your machine +- **Kernel Version Control**: Test on specific kernel versions +- **Easy Reset**: Delete VM and start fresh anytime +- **Reproducible Environment**: Consistent testing across systems +- **CI/CD Integration**: Automate testing in isolated environments + +## Testing Workflow + +1. **Unit Tests First**: Run `make unit` to test pure functions locally (no kernel required) +2. **QEMU Integration**: Use QEMU scripts to test full kernel module in isolation +3. **Dev Container**: Use provided dev container for consistent build environment +4. **Production**: Only deploy to production systems after thorough QEMU testing ## File Locations @@ -105,6 +131,12 @@ sudo make uninstall - Cloud-init: `scripts/qemu-env/user-data.yaml` - Disk image: `scripts/qemu-env/ubuntu-24.04.img` +## Additional Resources + +- **Main README**: [../README.md](../README.md) - Complete project documentation +- **Quick Reference**: Run `./scripts/quick-reference.sh` for command cheat sheet +- **Makefile Targets**: See main README for all available build and test targets + ## Cleanup To remove QEMU environment: @@ -117,3 +149,11 @@ To start fresh: rm -rf scripts/qemu-env/ ./scripts/qemu-setup.sh ``` + +## Notes + +- First boot takes 1-2 minutes for cloud-init to complete +- VM uses Ubuntu 24.04 LTS with Kernel 6.8+ +- KVM acceleration is automatically enabled if available +- Default credentials: ubuntu/ubuntu (change after first login) +- SSH port forwarding: Host port 2222 → VM port 22 diff --git a/scripts/quick-reference.sh b/scripts/quick-reference.sh index 3cabeb4..55ee2b5 100755 --- a/scripts/quick-reference.sh +++ b/scripts/quick-reference.sh @@ -1,12 +1,24 @@ #!/bin/bash -# Quick reference for common QEMU testing commands +# Quick reference for Linux Process Information Kernel Module testing cat << 'EOF' ╔══════════════════════════════════════════════════════════════╗ -║ QEMU Testing - Quick Reference ║ +║ Linux Process Info Kernel Module - Quick Reference ║ ╚══════════════════════════════════════════════════════════════╝ -SETUP (Run once) +LOCAL TESTING (No Kernel Required) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Build and run unit tests + make unit + + # Build everything + make all + + # Clean artifacts + make clean + + +QEMU SETUP (Run Once) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ./scripts/qemu-setup.sh @@ -19,8 +31,9 @@ START VM Exit: Ctrl+A then X -AUTO TEST (from host, in another terminal) +AUTO TEST (From Host, in Another Terminal) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Full automated test suite ./scripts/qemu-test.sh @@ -36,29 +49,47 @@ CONNECT TO VM scp -P 2222 ubuntu@localhost:~/file.txt ./ -MANUAL TESTING (inside VM) +MANUAL TESTING (Inside VM) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - # Build + # Build everything make clean && make all - # Install module - sudo insmod build/elf_det.ko + # Run unit tests (no kernel required) + make unit - # Check loaded + # Install kernel module + sudo make install + # OR: sudo insmod build/elf_det.ko + + # Verify module loaded lsmod | grep elf_det # Test with user program ./build/proc_elf_ctrl - # Or test manually + # Or test manually with proc interface echo "1" | sudo tee /proc/elf_det/pid sudo cat /proc/elf_det/det - # Check logs + # Check kernel logs sudo dmesg | tail -20 - # Unload - sudo rmmod elf_det + # Unload module + sudo make uninstall + # OR: sudo rmmod elf_det + + +BUILD TARGETS +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + make all - Build module and user program + make module - Build kernel module only + make user - Build user program only + make unit - Build and run unit tests + make install - Install kernel module + make uninstall - Remove kernel module + make test - Install module and run user program + make clean - Remove build artifacts + make help - Show all targets CLEANUP @@ -70,16 +101,46 @@ CLEANUP ./scripts/qemu-setup.sh +PROJECT STRUCTURE +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + src/elf_det.c - Kernel module source + src/proc_elf_ctrl.c - User program source + src/elf_det_tests.c - Unit tests for elf_det + src/proc_elf_ctrl_tests.c - Unit tests for proc_elf_ctrl + src/lib/ - Helper headers + build/ - Compiled artifacts + + TIPS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - First boot takes 1-2 minutes for cloud-init - - VM is completely isolated - crash won't affect host - - KVM acceleration used if available + - Always run 'make unit' first (no kernel required) + - Use QEMU for safe kernel module testing + - First VM boot takes 1-2 minutes for cloud-init + - VM is completely isolated - crashes won't affect host + - KVM acceleration used automatically if available - Change default password after first login + - Always unload module before rebuilding + + +TROUBLESHOOTING +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Module won't load + dmesg | tail -20 + ls /lib/modules/$(uname -r)/build + + # Can't connect to VM + ps aux | grep qemu + ssh -p 2222 ubuntu@localhost + + # Reset QEMU environment + rm -rf scripts/qemu-env/ + ./scripts/qemu-setup.sh MORE INFO ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - See: scripts/README.md + README.md - Complete project documentation + scripts/README.md - QEMU testing details + LICENSE - Dual BSD/GPL license EOF diff --git a/tests/elf_helpers_test.c b/src/elf_det_tests.c similarity index 100% rename from tests/elf_helpers_test.c rename to src/elf_det_tests.c diff --git a/tests/user_helpers_test.c b/src/proc_elf_ctrl_tests.c similarity index 100% rename from tests/user_helpers_test.c rename to src/proc_elf_ctrl_tests.c diff --git a/tests/test_elf_helpers.c b/tests/test_elf_helpers.c deleted file mode 100644 index 6ab6659..0000000 --- a/tests/test_elf_helpers.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "../src/lib/elf_helpers.h" -#include -#include - -int main(void) { - /* compute_usage_permyriad tests */ - assert(compute_usage_permyriad(0, 1000000ULL) == 0); - assert(compute_usage_permyriad(500000ULL, 1000000ULL) == 5000ULL); - assert(compute_usage_permyriad(250000ULL, 1000000ULL) == 2500ULL); - assert(compute_usage_permyriad(1000000ULL, 1000000ULL) == 10000ULL); - assert(compute_usage_permyriad(0, 0) == 0); - - /* compute_bss_range tests */ - unsigned long s = 0, e = 0; - assert(compute_bss_range(1000UL, 2000UL, &s, &e) == 1); - assert(s == 1000UL && e == 2000UL); - - assert(compute_bss_range(3000UL, 2000UL, &s, &e) == 0); - assert(s == 0UL && e == 0UL); - - puts("elf_helpers tests passed"); - return 0; -} diff --git a/tests/test_user_helpers.c b/tests/test_user_helpers.c deleted file mode 100644 index 4a2efa0..0000000 --- a/tests/test_user_helpers.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "../src/lib/user_helpers.h" -#include -#include -#include - -int main(void) { - /* Without env override */ - unsetenv("ELF_DET_PROC_DIR"); - char *p1 = build_proc_path("pid"); - assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); - free(p1); - - /* With env override */ - setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); - char *p2 = build_proc_path("det"); - assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); - free(p2); - - puts("user_helpers tests passed"); - return 0; -} From 787a22c3c421fa2498baa68de9f869e233e6ebb6 Mon Sep 17 00:00:00 2001 From: navidpadid Date: Sun, 4 Jan 2026 04:30:39 +0000 Subject: [PATCH 3/3] refactored --- README.md | 6 ++++-- scripts/quick-reference.sh | 3 ++- src/elf_det.c | 2 +- src/elf_det_tests.c | 2 +- src/{lib => }/elf_helpers.h | 0 src/proc_elf_ctrl.c | 2 +- src/proc_elf_ctrl_tests.c | 2 +- src/{lib => }/user_helpers.h | 0 8 files changed, 10 insertions(+), 7 deletions(-) rename src/{lib => }/elf_helpers.h (100%) rename src/{lib => }/user_helpers.h (100%) diff --git a/README.md b/README.md index af6b037..e2cb6e4 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ kernel_module/ │ ├── proc_elf_ctrl.c # User-space controller program │ ├── elf_det_tests.c # Unit tests for elf_det functions │ ├── proc_elf_ctrl_tests.c # Unit tests for proc_elf_ctrl helpers +│ ├── elf_helpers.h # Helper functions for CPU usage and BSS range +│ ├── user_helpers.h # Helper functions for path building │ └── Kbuild # Kernel build configuration ├── Makefile # Build system ├── .gitignore # Git ignore rules @@ -233,8 +235,8 @@ ELF_DET_PROC_DIR=/tmp/fakeproc ./build/proc_elf_ctrl 12345 Internally, path construction is handled via helper `build_proc_path()`. Helper headers used: -- `src/lib/user_helpers.h` – path building with env override -- `src/lib/elf_helpers.h` – pure functions for CPU usage and BSS range +- `src/user_helpers.h` – path building with env override +- `src/elf_helpers.h` – pure functions for CPU usage and BSS range ## Testing diff --git a/scripts/quick-reference.sh b/scripts/quick-reference.sh index 55ee2b5..6df98d0 100755 --- a/scripts/quick-reference.sh +++ b/scripts/quick-reference.sh @@ -107,7 +107,8 @@ PROJECT STRUCTURE src/proc_elf_ctrl.c - User program source src/elf_det_tests.c - Unit tests for elf_det src/proc_elf_ctrl_tests.c - Unit tests for proc_elf_ctrl - src/lib/ - Helper headers + src/elf_helpers.h - Helper functions (CPU, BSS) + src/user_helpers.h - Helper functions (path building) build/ - Compiled artifacts diff --git a/src/elf_det.c b/src/elf_det.c index d354b04..7cf921d 100644 --- a/src/elf_det.c +++ b/src/elf_det.c @@ -13,7 +13,7 @@ #include //for string libs #include //for task iteration #include //for task_cputime -#include "lib/elf_helpers.h" +#include "elf_helpers.h" MODULE_LICENSE("Dual BSD/GPL"); //module license diff --git a/src/elf_det_tests.c b/src/elf_det_tests.c index 96e63db..101de92 100644 --- a/src/elf_det_tests.c +++ b/src/elf_det_tests.c @@ -1,4 +1,4 @@ -#include "../src/lib/elf_helpers.h" +#include "elf_helpers.h" #include #include diff --git a/src/lib/elf_helpers.h b/src/elf_helpers.h similarity index 100% rename from src/lib/elf_helpers.h rename to src/elf_helpers.h diff --git a/src/proc_elf_ctrl.c b/src/proc_elf_ctrl.c index a1ac38f..b6167fd 100644 --- a/src/proc_elf_ctrl.c +++ b/src/proc_elf_ctrl.c @@ -2,7 +2,7 @@ #include #include #include -#include "lib/user_helpers.h" +#include "user_helpers.h" int main(int argc, char **argv) { diff --git a/src/proc_elf_ctrl_tests.c b/src/proc_elf_ctrl_tests.c index 0a066fa..bfe46e1 100644 --- a/src/proc_elf_ctrl_tests.c +++ b/src/proc_elf_ctrl_tests.c @@ -1,4 +1,4 @@ -#include "../src/lib/user_helpers.h" +#include "user_helpers.h" #include #include #include diff --git a/src/lib/user_helpers.h b/src/user_helpers.h similarity index 100% rename from src/lib/user_helpers.h rename to src/user_helpers.h