asterinas/test/apps/mmap/mmap_vmrss.c
2025-06-03 23:24:32 +08:00

153 lines
3.6 KiB
C

// SPDX-License-Identifier: MPL-2.0
#define _GNU_SOURCE
#include "../network/test.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define PAGE_SIZE 4096
#define NUM_PAGES 1024
#define TOTAL_SIZE (PAGE_SIZE * NUM_PAGES)
typedef enum rss_type {
anon,
file,
total,
} rss_type;
long get_vm_rss_kb(rss_type type)
{
pid_t pid = getpid();
char path[64];
snprintf(path, sizeof(path), "/proc/%d/status", pid);
FILE *f = fopen(path, "r");
if (!f) {
perror("fopen /proc/[pid]/status");
exit(1);
}
char line[256];
long rss_kb = -1;
const char *target_field = NULL;
switch (type) {
case anon:
target_field = "RssAnon:";
break;
case file:
target_field = "RssFile:";
break;
case total:
target_field = "VmRSS:";
break;
default:
perror("Unknown rss_type\n");
exit(1);
}
while (fgets(line, sizeof(line), f)) {
if (strncmp(line, target_field, strlen(target_field)) == 0) {
sscanf(line + strlen(target_field), "%ld", &rss_kb);
break;
}
}
fclose(f);
if (rss_kb < 0) {
fprintf(stderr, "Failed to parse VmRSS\n");
exit(1);
}
return rss_kb;
}
FN_TEST(rss_anon)
{
void *mem = mmap(NULL, TOTAL_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED) {
perror("mmap");
exit(1);
}
// The first call to `TEST_SUCC` and `get_vm_rss_kb()` may trigger
// lazy mapping of additional pages, such as shared libraries or files.
// These pages are not counted in RSS until they are actually accessed.
TEST_SUCC(get_vm_rss_kb(anon));
long rss_anon_before = TEST_SUCC(get_vm_rss_kb(anon));
long rss_file_before = TEST_SUCC(get_vm_rss_kb(file));
long rss_before = TEST_SUCC(get_vm_rss_kb(total));
// Trigger page faults
for (int i = 0; i < NUM_PAGES; ++i) {
volatile char *p = (char *)mem + i * PAGE_SIZE;
*p = 42;
}
TEST_RES(get_vm_rss_kb(anon),
_ret - rss_anon_before == NUM_PAGES * (PAGE_SIZE / 1024));
TEST_RES(get_vm_rss_kb(file), _ret == rss_file_before);
TEST_RES(get_vm_rss_kb(total),
_ret - rss_before == NUM_PAGES * (PAGE_SIZE / 1024));
TEST_SUCC(munmap(mem, TOTAL_SIZE));
TEST_RES(get_vm_rss_kb(anon), _ret == rss_anon_before);
TEST_RES(get_vm_rss_kb(file), _ret == rss_file_before);
TEST_RES(get_vm_rss_kb(total), _ret == rss_before);
}
END_TEST()
FN_TEST(rss_file)
{
const char *filename = "rss_test_file";
int fd = TEST_SUCC(open(filename, O_CREAT | O_RDWR, 0600));
TEST_SUCC(ftruncate(fd, TOTAL_SIZE));
// The first call to `TEST_SUCC` and `get_vm_rss_kb()` may trigger
// lazy mapping of additional pages, such as shared libraries or files.
// These pages are not counted in RSS until they are actually accessed.
TEST_SUCC(get_vm_rss_kb(anon));
long rss_anon_before = TEST_SUCC(get_vm_rss_kb(anon));
long rss_file_before = TEST_SUCC(get_vm_rss_kb(file));
long rss_before = TEST_SUCC(get_vm_rss_kb(total));
void *mem = mmap(NULL, TOTAL_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
perror("mmap");
exit(1);
}
// Trigger page faults
for (int i = 0; i < NUM_PAGES; ++i) {
volatile char x = *((char *)mem + i * PAGE_SIZE);
x++;
}
TEST_RES(get_vm_rss_kb(file),
_ret - rss_file_before == NUM_PAGES * (PAGE_SIZE / 1024));
TEST_RES(get_vm_rss_kb(anon), _ret == rss_anon_before);
TEST_RES(get_vm_rss_kb(total),
_ret - rss_before == NUM_PAGES * (PAGE_SIZE / 1024));
TEST_SUCC(munmap(mem, TOTAL_SIZE));
TEST_RES(get_vm_rss_kb(anon), _ret == rss_anon_before);
TEST_RES(get_vm_rss_kb(file), _ret == rss_file_before);
TEST_RES(get_vm_rss_kb(total), _ret == rss_before);
close(fd);
unlink(filename);
}
END_TEST()