[test] Add test for SHM
This commit is contained in:
		
							parent
							
								
									ed96ce55dd
								
							
						
					
					
						commit
						78450e58f8
					
				| @ -22,7 +22,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe | ||||
| 	truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \
 | ||||
| 	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \
 | ||||
| 	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | ||||
| 	spawn_attribute exec statfs random umask pgrp vfork mount flock utimes | ||||
| 	spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm | ||||
| # Benchmarks: need to be compiled and run by bench-% target
 | ||||
| BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										5
									
								
								test/shm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/shm/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| include ../test_common.mk | ||||
| 
 | ||||
| EXTRA_C_FLAGS := | ||||
| EXTRA_LINK_FLAGS := | ||||
| BIN_ARGS := | ||||
							
								
								
									
										414
									
								
								test/shm/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										414
									
								
								test/shm/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,414 @@ | ||||
| #define _GNU_SOURCE | ||||
| #include <sys/ipc.h> | ||||
| #include <sys/shm.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <fcntl.h> | ||||
| #include <time.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <spawn.h> | ||||
| #include <unistd.h> | ||||
| #include "test.h" | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Global definitions
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| #define S_IRWUSER   (S_IRUSR | S_IWUSR) | ||||
| 
 | ||||
| #define TEST_GET_SHMID_BY_KEY   0 | ||||
| #define TEST_PROCESS_COMMU      1 | ||||
| #define TEST_OPERATE_DESTOYED   2 | ||||
| 
 | ||||
| #define TEST_GET_SHMID_BY_KEY_ARGC  5 | ||||
| #define TEST_PROCESS_COMMU_ARGC     4 | ||||
| #define TEST_OPERATE_DESTOYED_ARGC  5 | ||||
| 
 | ||||
| #define ARG_BUF_SZ  64 | ||||
| #define PAGE_SIZE   (0x1000) | ||||
| 
 | ||||
| #define SUCCESS     1 | ||||
| #define FAIL        (-1) | ||||
| 
 | ||||
| const char prog_name[] = "/bin/shm"; | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Helper macro and function
 | ||||
| // ============================================================================
 | ||||
| #define INFO(fmt, ...)   do { \ | ||||
|     printf("\t\t[file: %s, line: %d, func: %s] " fmt, \ | ||||
|     __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ | ||||
| } while (0) | ||||
| 
 | ||||
| // Spawn child, and check the return value from child
 | ||||
| // Return `SUCCESS` if the execution succeeds,
 | ||||
| // return `FAIL` if fails
 | ||||
| static int execute_in_child(char **const child_argv) { | ||||
|     int ret; | ||||
|     pid_t child_pid; | ||||
|     int child_status; | ||||
| 
 | ||||
|     ret = posix_spawn(&child_pid, prog_name, NULL, NULL, (char **const)child_argv, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("Failed to spawn a child process"); | ||||
|     } | ||||
|     ret = waitpid(child_pid, &child_status, 0); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("Failed to waitpid() for child process"); | ||||
|     } | ||||
|     if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) { | ||||
|         INFO("The test in child failed\n"); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for shm
 | ||||
| // ============================================================================
 | ||||
| static int test_shmget_shmid_from_key(void) { | ||||
|     int ret, shmid; | ||||
|     key_t key; | ||||
|     size_t shm_size = PAGE_SIZE; | ||||
|     char *child_argv[TEST_GET_SHMID_BY_KEY_ARGC + 1]; | ||||
| 
 | ||||
|     srand(time(NULL)); | ||||
|     key = random(); | ||||
|     // Get a non-existent shm segment, should get the return value with ENOENT
 | ||||
|     ret = syscall(SYS_shmget, key, shm_size, S_IRWUSER); | ||||
|     if (ret != -1 || errno != ENOENT) { | ||||
|         INFO("shmget() should return ENOENT because the segment does not exist, ret: %d errno: %d\n", | ||||
|              ret, errno); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     // Create a new shm segment
 | ||||
|     shmid = syscall(SYS_shmget, key, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (shmid < 0) { | ||||
|         THROW_ERROR("shmget() cannot create the shm"); | ||||
|     } | ||||
| 
 | ||||
|     // Get the shmid by key in the same process
 | ||||
|     ret = syscall(SYS_shmget, key, shm_size, S_IRWUSER); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
|     if (ret != shmid) { | ||||
|         INFO("shmid mismatches, correct: %d actual: %d\n", shmid, ret); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     // Create a shm segment whose key is already attached to existed segment,
 | ||||
|     // should get the return value with EEXIST
 | ||||
|     ret = syscall(SYS_shmget, key, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (ret != -1 || errno != EEXIST) { | ||||
|         INFO("shmget() should return EEXIST because the segment already exists, ret: %d errno: %d\n", | ||||
|              ret, errno); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     // Spawn a new process
 | ||||
|     for (int i = 0; i < TEST_GET_SHMID_BY_KEY_ARGC; i++) { | ||||
|         child_argv[i] = (char *)malloc(ARG_BUF_SZ); | ||||
|     } | ||||
|     snprintf(child_argv[0], ARG_BUF_SZ, "%s", prog_name); | ||||
|     snprintf(child_argv[1], ARG_BUF_SZ, "%d", TEST_GET_SHMID_BY_KEY); | ||||
|     snprintf(child_argv[2], ARG_BUF_SZ, "%d", key); | ||||
|     snprintf(child_argv[3], ARG_BUF_SZ, "%d", shmid); | ||||
|     snprintf(child_argv[4], ARG_BUF_SZ, "%ld", shm_size); | ||||
|     child_argv[TEST_GET_SHMID_BY_KEY_ARGC] = NULL; | ||||
|     if ((ret = execute_in_child(child_argv)) != SUCCESS) { | ||||
|         return FAIL; | ||||
|     } | ||||
|     for (int i = 0; i < TEST_GET_SHMID_BY_KEY_ARGC; i++) { | ||||
|         free(child_argv[i]); | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_RMID, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("Cannot remove the segment"); | ||||
|     } | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static int test_process_communication() { | ||||
|     int shmid, ret; | ||||
|     size_t shm_size = PAGE_SIZE; | ||||
|     long random_num, *shm_addr; | ||||
|     char *child_argv[TEST_PROCESS_COMMU_ARGC + 1]; | ||||
| 
 | ||||
|     shmid = syscall(SYS_shmget, IPC_PRIVATE, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (shmid < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
|     shm_addr = (long *)syscall(SYS_shmat, shmid, NULL, 0); | ||||
|     if (shm_addr == (long *) -1) { | ||||
|         THROW_ERROR("shmat() cannot attach the shm"); | ||||
|     } | ||||
|     srandom(time(NULL)); | ||||
|     random_num = random(); | ||||
|     *shm_addr = random_num; | ||||
| 
 | ||||
|     // Spawn a new process
 | ||||
|     for (int i = 0; i < TEST_PROCESS_COMMU_ARGC; i++) { | ||||
|         child_argv[i] = (char *)malloc(ARG_BUF_SZ); | ||||
|     } | ||||
|     snprintf(child_argv[0], ARG_BUF_SZ, "%s", prog_name); | ||||
|     snprintf(child_argv[1], ARG_BUF_SZ, "%d", TEST_PROCESS_COMMU); | ||||
|     snprintf(child_argv[2], ARG_BUF_SZ, "%d", shmid); | ||||
|     snprintf(child_argv[3], ARG_BUF_SZ, "%ld", random_num); | ||||
|     child_argv[TEST_PROCESS_COMMU_ARGC] = NULL; | ||||
|     if ((ret = execute_in_child(child_argv)) != SUCCESS) { | ||||
|         return FAIL; | ||||
|     } | ||||
|     for (int i = 0; i < TEST_PROCESS_COMMU_ARGC; i++) { | ||||
|         free(child_argv[i]); | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmdt, (void *)shm_addr); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("shmdt() failed"); | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_RMID, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("Cannot remove the segment"); | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static int test_immediately_rmshm() { | ||||
|     int ret, shmid; | ||||
|     size_t shm_size = PAGE_SIZE; | ||||
|     struct shmid_ds buf; | ||||
| 
 | ||||
|     shmid = syscall(SYS_shmget, IPC_PRIVATE, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (shmid < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_RMID, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("Cannot remove the segment"); | ||||
|     } | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_STAT, NULL); | ||||
|     if (ret != -1 || errno != EINVAL) { | ||||
|         INFO("Should get errno with EINVAL even though the buf is empty, ret: %d errno: %d\n", | ||||
|              ret, errno); | ||||
|         return FAIL; | ||||
|     } | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_STAT, &buf); | ||||
|     if (ret != -1 || errno != EINVAL) { | ||||
|         INFO("The shared memory segment should be removed immediately since shm_nattach equals to 0, ret: %d errno: %d\n", | ||||
|              ret, errno); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static int test_operate_destroyed_shm() { | ||||
|     int shmid, ret; | ||||
|     size_t shm_size = PAGE_SIZE; | ||||
|     void *shm_addr; | ||||
|     key_t key; | ||||
|     char *child_argv[TEST_OPERATE_DESTOYED_ARGC + 1]; | ||||
| 
 | ||||
|     srand(time(NULL)); | ||||
|     key = random(); | ||||
|     shmid = syscall(SYS_shmget, key, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (shmid < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
|     shm_addr = (void *)syscall(SYS_shmat, shmid, NULL, 0); | ||||
|     if (shm_addr == (void *) -1) { | ||||
|         THROW_ERROR("shmat() cannot attach the shm"); | ||||
|     } | ||||
| 
 | ||||
|     // Mark the shared memory segment to be destroyed first
 | ||||
|     ret = syscall(SYS_shmctl, shmid, IPC_RMID, NULL); | ||||
| 
 | ||||
|     // Spawn a new process
 | ||||
|     for (int i = 0; i < TEST_OPERATE_DESTOYED_ARGC; i++) { | ||||
|         child_argv[i] = (char *)malloc(ARG_BUF_SZ); | ||||
|     } | ||||
|     snprintf(child_argv[0], ARG_BUF_SZ, "%s", prog_name); | ||||
|     snprintf(child_argv[1], ARG_BUF_SZ, "%d", TEST_OPERATE_DESTOYED); | ||||
|     snprintf(child_argv[2], ARG_BUF_SZ, "%d", key); | ||||
|     snprintf(child_argv[3], ARG_BUF_SZ, "%ld", shm_size); | ||||
|     snprintf(child_argv[4], ARG_BUF_SZ, "%d", shmid); | ||||
|     child_argv[TEST_OPERATE_DESTOYED_ARGC] = NULL; | ||||
|     if ((ret = execute_in_child(child_argv)) != SUCCESS) { | ||||
|         return FAIL; | ||||
|     } | ||||
|     for (int i = 0; i < TEST_OPERATE_DESTOYED_ARGC; i++) { | ||||
|         free(child_argv[i]); | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmdt, shm_addr); | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| // test_no_rmshm() should be place as the last test case
 | ||||
| //
 | ||||
| // Occlum checks whether all the memory segment is recycled when the LibOS exits,
 | ||||
| // to detect and prevent memory leaks.
 | ||||
| // Such test checks whether all the vmas allocated by `shm mod` are recycled when the LibOS exits,
 | ||||
| // even though no IPC_RMID is invoked for the shared memory segment.
 | ||||
| static int test_no_rmshm() { | ||||
|     int shmid; | ||||
|     size_t shm_size = PAGE_SIZE; | ||||
|     void *shm_addr; | ||||
| 
 | ||||
|     shmid = syscall(SYS_shmget, IPC_PRIVATE, shm_size, IPC_CREAT | IPC_EXCL | S_IRWUSER); | ||||
|     if (shmid < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
|     shm_addr = (void *)syscall(SYS_shmat, shmid, NULL, 0); | ||||
|     if (shm_addr == (long *) -1) { | ||||
|         THROW_ERROR("shmat() cannot attach the shm"); | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Funtion invoked in child process for inter-process communication
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int child_test_get_shmid_by_key(int argc, const char *argv[]) { | ||||
|     key_t key; | ||||
|     int ret, shmid; | ||||
|     size_t shm_size; | ||||
| 
 | ||||
|     if (argc != TEST_GET_SHMID_BY_KEY_ARGC) { | ||||
|         INFO("Invalid argument, argc: %d\n", argc); | ||||
|         return FAIL; | ||||
|     } | ||||
|     key = atoi(argv[2]); | ||||
|     shmid = atoi(argv[3]); | ||||
|     shm_size = atol(argv[4]); | ||||
| 
 | ||||
|     // Get the shmid by the key input from parent process
 | ||||
|     ret = syscall(SYS_shmget, key, shm_size, S_IRWUSER); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("shmget() cannot get the shm"); | ||||
|     } | ||||
|     if (ret != shmid) { | ||||
|         INFO("shmid get in child process mismatches that in parent process, correct: %d actual: %d\n", | ||||
|              shmid, key); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static int child_test_process_communication(int argc, const char *argv[]) { | ||||
|     int shmid, ret; | ||||
|     long random_num, *shm_ptr; | ||||
| 
 | ||||
|     if (argc != TEST_PROCESS_COMMU_ARGC) { | ||||
|         INFO("Invalid argument, argc: %d\n", argc); | ||||
|         return FAIL; | ||||
|     } | ||||
|     shmid = atoi(argv[2]); | ||||
|     random_num = atol(argv[3]); | ||||
| 
 | ||||
|     // Check whether the value in the shared memory segment equals to
 | ||||
|     // that written in parent process
 | ||||
|     shm_ptr = (long *)syscall(SYS_shmat, shmid, NULL, 0); | ||||
|     if (shm_ptr == (long *) -1) { | ||||
|         THROW_ERROR("shmat() cannot attach to the shm"); | ||||
|     } | ||||
|     if (*shm_ptr != random_num) { | ||||
|         INFO("Data in shm mismatches, correct: %ld actual: %ld\n", random_num, *shm_ptr); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     ret = syscall(SYS_shmdt, (void *)shm_ptr); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("shmdt() failed"); | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static int child_test_operate_destroyed_shm(int argc, const char *argv[]) { | ||||
|     int shmid, ret; | ||||
|     key_t key; | ||||
|     size_t shm_size; | ||||
|     void *shm_addr; | ||||
| 
 | ||||
|     if (argc != TEST_OPERATE_DESTOYED_ARGC) { | ||||
|         INFO("Invalid argument, argc: %d\n", argc); | ||||
|         return FAIL; | ||||
|     } | ||||
|     key = atoi(argv[2]); | ||||
|     shm_size = atol(argv[3]); | ||||
|     shmid = atoi(argv[4]); | ||||
| 
 | ||||
|     ret = syscall(SYS_shmget, key, shm_size, S_IRWUSER); | ||||
|     if (ret != -1 || errno != ENOENT) { | ||||
|         INFO("shmget() should return ENOENT because the segment is marked to be destoyed, ret: %d errno: %d\n", | ||||
|              ret, errno); | ||||
|         return FAIL; | ||||
|     } | ||||
| 
 | ||||
|     shm_addr = (void *)syscall(SYS_shmat, shmid, NULL, 0); | ||||
|     if (shm_addr == (void *) -1) { | ||||
|         THROW_ERROR("shmat() cannot attach the shm"); | ||||
|     } | ||||
| 
 | ||||
|     if ((ret = syscall(SYS_shmdt, shm_addr)) != 0) { | ||||
|         THROW_ERROR("shmdt() failed"); | ||||
|     } | ||||
| 
 | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static test_case_t test_cases[] = { | ||||
|     TEST_CASE(test_shmget_shmid_from_key), | ||||
|     TEST_CASE(test_process_communication), | ||||
|     TEST_CASE(test_immediately_rmshm), | ||||
|     TEST_CASE(test_operate_destroyed_shm), | ||||
|     // test_no_rmshm() should be place as the last test case
 | ||||
|     TEST_CASE(test_no_rmshm), | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, const char *argv[]) { | ||||
|     if (argc == 1) { | ||||
|         // Parent process will arrive here
 | ||||
|         return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||
|     } else { | ||||
|         // Child process will arrive here
 | ||||
|         int option = atoi(argv[1]), ret; | ||||
|         switch (option) { | ||||
|             case TEST_GET_SHMID_BY_KEY: | ||||
|                 ret = child_test_get_shmid_by_key(argc, argv); | ||||
|                 break; | ||||
|             case TEST_PROCESS_COMMU: | ||||
|                 ret = child_test_process_communication(argc, argv); | ||||
|                 break; | ||||
|             case TEST_OPERATE_DESTOYED: | ||||
|                 ret = child_test_operate_destroyed_shm(argc, argv); | ||||
|                 break; | ||||
|             default: | ||||
|                 INFO("Invalid option: %d\n", option); | ||||
|                 ret = FAIL; | ||||
|         } | ||||
|         if (ret == SUCCESS) { | ||||
|             return 0; | ||||
|         } else { | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user