| LTP C Test API |
| ============== |
| |
| NOTE: See also |
| https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], |
| https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial[C Test Case Tutorial], |
| https://github.com/linux-test-project/ltp/wiki/Shell-Test-API[Shell Test API]. |
| |
| 1 Writing a test in C |
| --------------------- |
| |
| 1.1 Basic test structure |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Let's start with an example, following code is a simple test for a 'getenv()'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| /*\ |
| * [Description] |
| * Tests basic functionality of getenv(). |
| * |
| * - create an env variable and verify that getenv() can get it |
| * - call getenv() with nonexisting variable name, check that it returns NULL |
| */ |
| |
| #include "tst_test.h" |
| |
| #define ENV1 "LTP_TEST_ENV" |
| #define ENV2 "LTP_TEST_THIS_DOES_NOT_EXIST" |
| #define ENV_VAL "val" |
| |
| static void setup(void) |
| { |
| if (setenv(ENV1, ENV_VAL, 1)) |
| tst_brk(TBROK | TERRNO, "setenv() failed"); |
| } |
| |
| static void run(void) |
| { |
| char *ret; |
| |
| ret = getenv(ENV1); |
| |
| if (!ret) { |
| tst_res(TFAIL, "getenv(" ENV1 ") = NULL"); |
| goto next; |
| } |
| |
| if (!strcmp(ret, ENV_VAL)) { |
| tst_res(TPASS, "getenv(" ENV1 ") = '"ENV_VAL "'"); |
| } else { |
| tst_res(TFAIL, "getenv(" ENV1 ") = '%s', expected '" |
| ENV_VAL "'", ret); |
| } |
| |
| next: |
| ret = getenv(ENV2); |
| |
| if (ret) |
| tst_res(TFAIL, "getenv(" ENV2 ") = '%s'", ret); |
| else |
| tst_res(TPASS, "getenv(" ENV2 ") = NULL"); |
| } |
| |
| static struct tst_test test = { |
| .test_all = run, |
| .setup = setup, |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Each test includes the 'tst_test.h' header and must define the 'struct |
| tst_test test' structure. |
| |
| The overall test initialization is done in the 'setup()' function. |
| |
| The overall cleanup is done in a 'cleanup()' function. Here 'cleanup()' is |
| omitted as the test does not have anything to clean up. If cleanup is set in |
| the test structure it's called on test exit just before the test library |
| cleanup. That especially means that cleanup can be called at any point in a |
| test execution. For example even when a test setup step has failed, therefore |
| the 'cleanup()' function must be able to cope with unfinished initialization, |
| and so on. |
| |
| The test itself is done in the 'test()' function. The test function must work |
| fine if called in a loop. |
| |
| There are two types of a test function pointers in the test structure. The |
| first one is a '.test_all' pointer that is used when test is implemented as a |
| single function. Then there is a '.test' function along with the number of |
| tests '.tcnt' that allows for more detailed result reporting. If the '.test' |
| pointer is set the function is called '.tcnt' times with an integer parameter |
| in range of [0, '.tcnt' - 1]. |
| |
| IMPORTANT: Only one of '.test' and '.test_all' can be set at a time. |
| |
| Each test has a limit on how long it can run and the limit composes of two |
| parts max_runtime and timeout. The max_runtime is a limit for how long can the |
| '.test_all' or a set of '.test' functions take and the timeout is static part |
| that should cover the duration of test setup and cleanup plus some safety. |
| |
| Any test that runs for more than a second or two has to make sure to: |
| |
| - set the runtime either by setting the '.max_runtime' in tst_test or by |
| calling 'tst_set_max_runtime()' in the test setup |
| |
| - monitor remaning runtime by regular calls to 'tst_remaining_runtime()' and |
| exit when runtime has been used up |
| |
| Test is free to exit before max_runtime has been used up for example when |
| minimal number of iteration was finished. |
| |
| The limit is applied to a single call of the '.test_all' function that means |
| that for example when '.test_variants' or '.all_filesystems' is set the whole |
| test will be limited by 'variants * (max_runtime + timeout)' seconds and the |
| test runtime will be likely close to 'variants * max_runtime' seconds. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| /* |
| * Returns number of seconds or zero in case that runtime has been used up. |
| */ |
| |
| int tst_remaining_runtime(void); |
| ------------------------------------------------------------------------------- |
| |
| LAPI headers |
| ++++++++++++ |
| |
| Use our LAPI headers ('include "lapi/foo.h"') to keep compatibility with old |
| distributions. LAPI header should always include original header. Older linux |
| headers were problematic, therefore we preferred to use libc headers. There are |
| still some bugs when combining certain glibc headers with linux headers, see |
| https://sourceware.org/glibc/wiki/Synchronizing_Headers. |
| |
| A word about the cleanup() callback |
| +++++++++++++++++++++++++++++++++++ |
| |
| There are a few rules that needs to be followed in order to write correct |
| cleanup() callback. |
| |
| 1. Free only resources that were initialized. Keep in mind that callback can |
| be executed at any point in the test run. |
| |
| 2. Make sure to free resources in the reverse order they were |
| initialized. (Some of the steps may not depend on others and everything |
| will work if there were swapped but let's keep it in order.) |
| |
| The first rule may seem complicated at first however, on the contrary, it's |
| quite easy. All you have to do is to keep track of what was already |
| initialized. For example file descriptors needs to be closed only if they were |
| assigned a valid file descriptor. For most of the things you need to create |
| extra flag that is set right after successful initialization though. Consider, |
| for example, test setup below. |
| |
| We also prefer cleaning up resources that would otherwise be released on the |
| program exit. There are two main reasons for this decision. Resources such as |
| file descriptors and mmaped memory could block umounting a block device in |
| cases where the test library has mounted a filesystem for the test temporary |
| directory. Not freeing allocated memory would upset static analysis and tools |
| such as valgrind and produce false-positives when checking for leaks in the |
| libc and other low level libraries. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static int fd0, fd1, mount_flag; |
| |
| #define MNTPOINT "mntpoint" |
| #define FILE1 "mntpoint/file1" |
| #define FILE2 "mntpoint/file2" |
| |
| static void setup(void) |
| { |
| SAFE_MKDIR(MNTPOINT, 0777); |
| SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL); |
| SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, 0); |
| mount_flag = 1; |
| |
| fd0 = SAFE_OPEN(cleanup, FILE1, O_CREAT | O_RDWR, 0666); |
| fd1 = SAFE_OPEN(cleanup, FILE2, O_CREAT | O_RDWR, 0666); |
| } |
| ------------------------------------------------------------------------------- |
| |
| In this case the 'cleanup()' function may be invoked when any of the 'SAFE_*' |
| macros has failed and therefore must be able to work with unfinished |
| initialization as well. Since global variables are initialized to zero we can |
| just check that fd > 0 before we attempt to close it. The mount function |
| requires extra flag to be set after device was successfully mounted. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static void cleanup(void) |
| { |
| if (fd1 > 0) |
| SAFE_CLOSE(fd1); |
| |
| if (fd0 > 0) |
| SAFE_CLOSE(fd0); |
| |
| if (mount_flag && tst_umouont(MNTPOINT)) |
| tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT); |
| } |
| ------------------------------------------------------------------------------- |
| |
| IMPORTANT: 'SAFE_MACROS()' used in cleanup *do not* exit the test. Failure |
| only produces a warning and the 'cleanup()' carries on. This is |
| intentional as we want to execute as much 'cleanup()' as possible. |
| |
| WARNING: Calling tst_brk() in test 'cleanup()' does not exit the test as well |
| and 'TBROK' is converted to 'TWARN'. |
| |
| NOTE: Creation and removal of the test temporary directory is handled in |
| the test library and the directory is removed recursively. Therefore |
| we do not have to remove files and directories in the test cleanup. |
| |
| 1.2 Basic test interface |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| void tst_res(int ttype, char *arg_fmt, ...); |
| ------------------------------------------------------------------------------- |
| |
| Printf-like function to report test result, it's mostly used with ttype: |
| |
| |============================== |
| | 'TPASS' | Test has passed. |
| | 'TFAIL' | Test has failed. |
| | 'TINFO' | General message. |
| | 'TDEBUG' | Debug message (new C API only, printed with '-D' or via 'LTP_ENABLE_DEBUG=1' or 'y' |
| environment variable), only for messages which would be too verbose for normal run. |
| | 'TWARN' | Something went wrong but we decided to continue. Mostly used in cleanup functions. |
| |============================== |
| |
| The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print |
| 'errno', 'TST_ERR' respectively. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| void tst_brk(int ttype, char *arg_fmt, ...); |
| ------------------------------------------------------------------------------- |
| |
| Printf-like function to report error and exit the test, it can be used with ttype: |
| |
| |============================================================ |
| | 'TBROK' | Something has failed in test preparation phase. |
| | 'TCONF' | Test is not appropriate for current configuration |
| (syscall not implemented, unsupported arch, ...) |
| |============================================================ |
| |
| The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print |
| 'errno', 'TST_ERR' respectively. |
| |
| There are also 'TST_EXP_*()' macros that can simplify syscall unit tests to a |
| single line, use them whenever possible. These macros take a function call as |
| the first parameter and a printf-like format string and parameters as well. |
| These test macros then expand to a code that runs the call, checks the return |
| value and errno and reports the test result. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static void run(void) |
| { |
| ... |
| TST_EXP_PASS(stat(fname, &statbuf), "stat(%s, ...)", fname); |
| |
| if (!TST_PASS) |
| return; |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| The 'TST_EXP_PASS()' can be used for calls that return -1 on failure and 0 on |
| success. It will check for the return value and reports failure if the return |
| value is not equal to 0. The call also sets the 'TST_PASS' variable to 1 if |
| the call succeeeded. |
| |
| As seen above, this and similar macros take optional variadic arguments. These |
| begin with a format string and then appropriate values to be formatted. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static void run(void) |
| { |
| ... |
| TST_EXP_FD(open(fname, O_RDONLY), "open(%s, O_RDONLY)", fname); |
| |
| SAFE_CLOSE(TST_RET); |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| The 'TST_EXP_FD()' is the same as 'TST_EXP_PASS()' the only difference is that |
| the return value is expected to be a file descriptor so the call passes if |
| positive integer is returned. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static void run(void) |
| { |
| ... |
| TST_EXP_FAIL(stat(fname, &statbuf), ENOENT, "stat(%s, ...)", fname); |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| The 'TST_EXP_FAIL()' is similar to 'TST_EXP_PASS()' but it fails the test if |
| the call haven't failed with -1 and 'errno' wasn't set to the expected one |
| passed as the second argument. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static void run(void) |
| { |
| ... |
| TST_EXP_FAIL2(msgget(key, flags), EINVAL, "msgget(%i, %i)", key, flags); |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| The 'TST_EXP_FAIL2()' is the same as 'TST_EXP_FAIL()' except the return value is |
| expected to be non-negative integer if call passes. These macros build upon the |
| +TEST()+ macro and associated variables. |
| |
| 'TST_EXP_FAIL_SILENT()' and 'TST_EXP_FAIL2_SILENT()' variants are less verbose |
| and do not print TPASS messages when SCALL fails as expected. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| TEST(socket(AF_INET, SOCK_RAW, 1)); |
| if (TST_RET > -1) { |
| tst_res(TFAIL, "Created raw socket"); |
| SAFE_CLOSE(TST_RET); |
| } else if (TST_ERR != EPERM) { |
| tst_res(TFAIL | TTERRNO, |
| "Failed to create socket for wrong reason"); |
| } else { |
| tst_res(TPASS | TTERRNO, "Didn't create raw socket"); |
| } |
| ------------------------------------------------------------------------------- |
| |
| The +TEST+ macro sets +TST_RET+ to its argument's return value and +TST_ERR+ to |
| +errno+. The +TTERNO+ flag can be used to print the error number's symbolic |
| value. |
| |
| No LTP library function or macro, except those in 'tst_test_macros.h', will |
| write to these variables (rule 'LTP-002'). So their values will not be changed |
| unexpectedly. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| TST_EXP_POSITIVE(wait(&status)); |
| |
| if (!TST_PASS) |
| return; |
| ------------------------------------------------------------------------------- |
| |
| If the return value of 'wait' is positive or zero, this macro will print a pass |
| result and set +TST_PASS+ appropriately. If the return value is negative, then |
| it will print fail. There are many similar macros to those shown here, please |
| see 'tst_test_macros.h'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| TST_EXP_EQ_LI(val1, val2); |
| TST_EXP_EQ_UI(val1, val2); |
| TST_EXP_EQ_SZ(val1, val2); |
| TST_EXP_EQ_SSZ(val1, val2); |
| |
| /* Use as */ |
| TST_EXP_EQ_LI(sig_caught, SIGCHLD); |
| ------------------------------------------------------------------------------- |
| |
| Set of macros for different integer type comparsions. These macros print the |
| variable names as well as values in both pass and fail scenarios. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| const char *tst_strsig(int sig); |
| ------------------------------------------------------------------------------- |
| |
| Return the given signal number's corresponding string. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| const char *tst_strerrno(int err); |
| ------------------------------------------------------------------------------- |
| |
| Return the given errno number's corresponding string. Using this function to |
| translate 'errno' values to strings is preferred. You should not use the |
| 'strerror()' function in the testcases. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| const char *tst_strstatus(int status); |
| ------------------------------------------------------------------------------- |
| |
| Returns string describing the status as returned by 'wait()'. |
| |
| WARNING: This function is not thread safe. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| void tst_set_max_runtime(int max_runtime); |
| ------------------------------------------------------------------------------- |
| |
| Allows for setting max_runtime per test iteration dynamically in the test 'setup()', |
| the timeout is specified in seconds. There are a few testcases whose runtime |
| can vary arbitrarily, these can disable timeouts by setting it to |
| TST_UNLIMITED_RUNTIME. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| void tst_flush(void); |
| ------------------------------------------------------------------------------- |
| |
| Flush output streams, handling errors appropriately. |
| |
| This function is rarely needed when you have to flush the output streams |
| before calling 'fork()' or 'clone()'. Note that the 'SAFE_FORK()' and 'SAFE_CLONE()' |
| calls this function automatically. See 2.4 FILE buffers and fork() for explanation |
| why is this needed. |
| |
| 1.3 Test temporary directory |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| If '.needs_tmpdir' is set to '1' in the 'struct tst_test' unique test |
| temporary is created and it's set as the test working directory. Tests *MUST |
| NOT* create temporary files outside that directory. The flag is not needed to |
| be set when use these flags: '.all_filesystems', '.format_device', '.mntpoint', |
| '.mount_device' '.needs_checkpoints', '.needs_device', '.resource_file' |
| (these flags imply creating temporary directory). |
| |
| IMPORTANT: Close all file descriptors (that point to files in test temporary |
| directory, even the unlinked ones) either in the 'test()' function |
| or in the test 'cleanup()' otherwise the test may break temporary |
| directory removal on NFS (look for "NFS silly rename"). |
| |
| 1.4 Safe macros |
| ~~~~~~~~~~~~~~~ |
| |
| Safe macros aim to simplify error checking in test preparation. Instead of |
| calling system API functions, checking for their return value and aborting the |
| test if the operation has failed, you just use corresponding safe macro. |
| |
| Use them whenever it's possible. |
| |
| Instead of writing: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| fd = open("/dev/null", O_RDONLY); |
| if (fd < 0) |
| tst_brk(TBROK | TERRNO, "opening /dev/null failed"); |
| ------------------------------------------------------------------------------- |
| |
| You write just: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| fd = SAFE_OPEN("/dev/null", O_RDONLY); |
| ------------------------------------------------------------------------------- |
| |
| IMPORTANT: The 'SAFE_CLOSE()' function also sets the passed file descriptor to -1 |
| after it's successfully closed. |
| |
| They can also simplify reading and writing of sysfs files, you can, for |
| example, do: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%lu", &pid_max); |
| ------------------------------------------------------------------------------- |
| |
| See 'include/tst_safe_macros.h', 'include/tst_safe_stdio.h' and |
| 'include/tst_safe_file_ops.h' and 'include/tst_safe_net.h' for a complete list. |
| |
| 1.5 Test specific command line options |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| struct tst_option { |
| char *optstr; |
| char **arg; |
| char *help; |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Test specific command line parameters can be passed with the 'NULL' terminated |
| array of 'struct tst_option'. The 'optstr' is the command line option i.e. "o" |
| or "o:" if option has a parameter. Only short options are supported. The 'arg' |
| is where 'optarg' is stored upon match. If option has no parameter it's set to |
| non-'NULL' value if option was present. The 'help' is a short help string. |
| |
| NOTE: The test parameters must not collide with common test parameters defined |
| in the library the currently used ones are +-i+, +-I+, +-C+, and +-h+. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| int tst_parse_int(const char *str, int *val, int min, int max); |
| int tst_parse_long(const char *str, long *val, long min, long max); |
| int tst_parse_float(const char *str, float *val, float min, float max); |
| int tst_parse_filesize(const char *str, long long *val, long long min, long long max); |
| ------------------------------------------------------------------------------- |
| |
| Helpers for parsing the strings returned in the 'struct tst_option'. |
| |
| Helpers return zero on success and 'errno', mostly 'EINVAL' or 'ERANGE', on |
| failure. |
| |
| Helpers functions are no-op if 'str' is 'NULL'. |
| |
| The valid range for result includes both 'min' and 'max'. |
| |
| In particular, 'tst_parse_filesize' function accepts prefix multiplies such as |
| "k/K for kilobytes, "m/M" for megabytes and "g/G" for gigabytes. For example, |
| 10K are converted into 10240 bytes. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include <limits.h> |
| #include "tst_test.h" |
| |
| static char *str_threads; |
| static int threads = 10; |
| |
| static void setup(void) |
| { |
| if (tst_parse_int(str_threads, &threads, 1, INT_MAX)) |
| tst_brk(TBROK, "Invalid number of threads '%s'", str_threads); |
| |
| ... |
| } |
| |
| static void test_threads(void) |
| { |
| ... |
| |
| for (i = 0; i < threads; i++) { |
| ... |
| } |
| |
| ... |
| } |
| |
| static struct tst_test test = { |
| ... |
| .options = (struct tst_option[]) { |
| {"t:", &str_threads, "Number of threads (default 10)"}, |
| {}, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| |
| 1.6 Runtime kernel version detection |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Testcases for newly added kernel functionality require kernel newer than a |
| certain version to run. All you need to skip a test on older kernels is to |
| set the '.min_kver' string in the 'struct tst_test' to a minimal required |
| kernel version, e.g. '.min_kver = "4.10.0"'. |
| |
| For more complicated operations such as skipping a test for a certain range |
| of kernel versions, following functions could be used: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| int tst_kvercmp(int r1, int r2, int r3); |
| |
| struct tst_kern_exv { |
| char *dist_name; |
| char *extra_ver; |
| }; |
| |
| int tst_kvercmp2(int r1, int r2, int r3, struct tst_kern_exv *vers); |
| ------------------------------------------------------------------------------- |
| |
| These two functions are intended for runtime kernel version detection. They |
| parse the output from 'uname()' and compare it to the passed values. |
| |
| The return value is similar to the 'strcmp()' function, i.e. zero means equal, |
| negative value means that the kernel is older than the expected value and |
| positive means that it's newer. |
| |
| The second function 'tst_kvercmp2()' allows for specifying per-vendor table of |
| kernel versions as vendors typically backport fixes to their kernels and the |
| test may be relevant even if the kernel version does not suggests so. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| if (tst_kvercmp(5, 19, 0) >= 0) |
| tst_res(TCONF, "Test valid only for kernel < 5.19"); |
| |
| static struct tst_kern_exv kvers[] = { |
| { "UBUNTU", "4.4.0-48.69" }, |
| { NULL, NULL}, |
| }; |
| |
| if (tst_kvercmp2(4, 4, 27, kvers) < 0) |
| /* code for kernel < v4.4.27 or ubuntu kernel < 4.4.0-48.69 */ |
| ------------------------------------------------------------------------------- |
| |
| WARNING: The shell 'tst_kvercmp' maps the result into unsigned integer - the |
| process exit value. |
| |
| NOTE: See also LTP |
| https://github.com/linux-test-project/ltp/wiki/Supported-kernel,-libc,-toolchain-versions#13-minimal-supported-kernel-version[minimal supported kernel version]. |
| |
| 1.7 Fork()-ing |
| ~~~~~~~~~~~~~~ |
| |
| Be wary that if the test forks and there were messages printed by the |
| 'tst_*()' interfaces, the data may still be in libc/kernel buffers and these |
| *ARE NOT* flushed automatically. |
| |
| This happens when 'stdout' gets redirected to a file. In this case, the |
| 'stdout' is not line buffered, but block buffered. Hence after a fork content |
| of the buffers will be printed by the parent and each of the children. |
| |
| To avoid that you should use 'SAFE_FORK()', 'SAFE_CLONE()' or 'tst_clone()'. |
| |
| IMPORTANT: You have to set the '.forks_child' flag in the test structure |
| if your testcase forks or calls 'SAFE_CLONE()'. |
| |
| 1.8 Doing the test in the child process |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Results reported by 'tst_res()' are propagated to the parent test process via |
| block of shared memory. |
| |
| Calling 'tst_brk()' causes child process to exit with non-zero exit value. |
| Which means that it's safe to use 'SAFE_*()' macros in the child processes as |
| well. |
| |
| Children that outlive the 'test()' function execution are waited for in the |
| test library. Unclean child exit (killed by signal, non-zero exit value, etc.) |
| will cause the main test process to exit with 'tst_brk()', which especially |
| means that 'TBROK' propagated from a child process will cause the whole test |
| to exit with 'TBROK'. |
| |
| If a test needs a child that segfaults or does anything else that cause it to |
| exit uncleanly all you need to do is to wait for such children from the |
| 'test()' function so that it's reaped before the main test exits the 'test()' |
| function. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| void tst_reap_children(void); |
| ------------------------------------------------------------------------------- |
| |
| The 'tst_reap_children()' function makes the process wait for all of its |
| children and exits with 'tst_brk(TBROK, ...)' if any of them returned |
| a non zero exit code. |
| |
| When using 'SAFE_CLONE' or 'tst_clone', this may not work depending on |
| the parameters passed to clone. The following call to 'SAFE_CLONE' is |
| identical to 'fork()', so will work as expected. |
| |
| [source,c] |
| -------------------------------------------------------------------------------- |
| const struct tst_clone_args args = { |
| .exit_signal = SIGCHLD, |
| }; |
| |
| SAFE_CLONE(&args); |
| -------------------------------------------------------------------------------- |
| |
| If 'exit_signal' is set to something else, then this will break |
| 'tst_reap_children'. It's not expected that all parameters to clone will |
| work with the LTP library unless specific action is taken by the test code. |
| |
| .Using 'tst_res()' from binaries started by 'exec()' |
| [source,c] |
| ------------------------------------------------------------------------------- |
| /* test.c */ |
| #define _GNU_SOURCE |
| #include <unistd.h> |
| #include "tst_test.h" |
| |
| static void do_test(void) |
| { |
| char *const argv[] = {"test_exec_child", NULL}; |
| char path[4096]; |
| |
| if (tst_get_path("test_exec_child", path, sizeof(path))) |
| tst_brk(TCONF, "Couldn't find test_exec_child in $PATH"); |
| |
| execve(path, argv, environ); |
| |
| tst_res(TFAIL | TERRNO, "EXEC!"); |
| } |
| |
| static struct tst_test test = { |
| .test_all = do_test, |
| .child_needs_reinit = 1, |
| }; |
| |
| /* test_exec_child.c */ |
| #define TST_NO_DEFAULT_MAIN |
| #include "tst_test.h" |
| |
| int main(void) |
| { |
| tst_reinit(); |
| tst_res(TPASS, "Child passed!"); |
| return 0; |
| } |
| ------------------------------------------------------------------------------- |
| |
| The 'tst_res()' function can be also used from binaries started by 'exec()', |
| the parent test process has to set the '.child_needs_reinit' flag so that the |
| library prepares for it and has to make sure the 'LTP_IPC_PATH' environment |
| variable is passed down, then the very first thing the program has to call in |
| 'main()' is 'tst_reinit()' that sets up the IPC. |
| |
| 1.9 Fork() and Parent-child synchronization |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| As LTP tests are written for Linux, most of the tests involve fork()-ing and |
| parent-child process synchronization. LTP includes a checkpoint library that |
| provides wait/wake futex based functions. |
| |
| In order to use checkpoints the '.needs_checkpoints' flag in the 'struct |
| tst_test' must be set to '1', this causes the test library to initialize |
| checkpoints before the 'test()' function is called. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| TST_CHECKPOINT_WAIT(id) |
| |
| TST_CHECKPOINT_WAIT2(id, msec_timeout) |
| |
| TST_CHECKPOINT_WAKE(id) |
| |
| TST_CHECKPOINT_WAKE2(id, nr_wake) |
| |
| TST_CHECKPOINT_WAKE_AND_WAIT(id) |
| ------------------------------------------------------------------------------- |
| |
| The checkpoint interface provides pair of wake and wait functions. The 'id' is |
| unsigned integer which specifies checkpoint to wake/wait for. As a matter of |
| fact it's an index to an array stored in a shared memory, so it starts on |
| '0' and there should be enough room for at least of hundred of them. |
| |
| The 'TST_CHECKPOINT_WAIT()' and 'TST_CHECKPOINT_WAIT2()' suspends process |
| execution until it's woken up or until timeout is reached. |
| |
| The 'TST_CHECKPOINT_WAKE()' wakes one process waiting on the checkpoint. |
| If no process is waiting the function retries until it success or until |
| timeout is reached. |
| |
| If timeout has been reached process exits with appropriate error message (uses |
| 'tst_brk()'). |
| |
| The 'TST_CHECKPOINT_WAKE2()' does the same as 'TST_CHECKPOINT_WAKE()' but can |
| be used to wake precisely 'nr_wake' processes. |
| |
| The 'TST_CHECKPOINT_WAKE_AND_WAIT()' is a shorthand for doing wake and then |
| immediately waiting on the same checkpoint. |
| |
| Child processes created via 'SAFE_FORK()' are ready to use the checkpoint |
| synchronization functions, as they inherited the mapped page automatically. |
| |
| Child processes started via 'exec()', or any other processes not forked from |
| the test process must initialize the checkpoint by calling 'tst_reinit()'. |
| |
| For the details of the interface, look into the 'include/tst_checkpoint.h'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| /* |
| * Waits for process state change. |
| * |
| * The state is one of the following: |
| * |
| * R - process is running |
| * S - process is sleeping |
| * D - process sleeping uninterruptibly |
| * Z - zombie process |
| * T - process is traced |
| */ |
| TST_PROCESS_STATE_WAIT(pid, state, msec_timeout) |
| ------------------------------------------------------------------------------- |
| |
| The 'TST_PROCESS_STATE_WAIT()' waits until process 'pid' is in requested |
| 'state' or timeout is reached. The call polls +/proc/pid/stat+ to get this |
| information. A timeout of 0 will wait infinitely. |
| |
| On timeout -1 is returned and errno set to ETIMEDOUT. |
| |
| It's mostly used with state 'S' which means that process is sleeping in kernel |
| for example in 'pause()' or any other blocking syscall. |
| |
| 1.10 Signals and signal handlers |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| If you need to use signal handlers, keep the code short and simple. Don't |
| forget that the signal handler is called asynchronously and can interrupt the |
| code execution at any place. |
| |
| This means that problems arise when global state is changed both from the test |
| code and signal handler, which will occasionally lead to: |
| |
| * Data corruption (data gets into inconsistent state), this may happen, for |
| example, for any operations on 'FILE' objects. |
| |
| * Deadlock, this happens, for example, if you call 'malloc(2)', 'free(2)', |
| etc. from both the test code and the signal handler at the same time since |
| 'malloc' has global lock for it's internal data structures. (Be wary that |
| 'malloc(2)' is used by the libc functions internally too.) |
| |
| * Any other unreproducible and unexpected behavior. |
| |
| Quite common mistake is to call 'exit(3)' from a signal handler. Note that this |
| function is not signal-async-safe as it flushes buffers, etc. If you need to |
| exit a test immediately from a signal handler use '_exit(2)' instead. |
| |
| TIP: See 'man 7 signal' for the list of signal-async-safe functions. |
| |
| If a signal handler sets a variable, its declaration must be 'volatile', |
| otherwise compiler may misoptimize the code. This is because the variable may |
| not be changed in the compiler code flow analysis. There is 'sig_atomic_t' |
| type defined in C99 but this one *DOES NOT* imply 'volatile' (it's just a |
| 'typedef' to 'int'). So the correct type for a flag that is changed from a |
| signal handler is either 'volatile int' or 'volatile sig_atomic_t'. |
| |
| If a crash (e.g. triggered by signal SIGSEGV) is expected in testing, you |
| can avoid creation of core files by calling 'tst_no_corefile()' function. |
| This takes effect for process (and its children) which invoked it, unless |
| they subsequently modify RLIMIT_CORE. |
| |
| Note that LTP library will reap any processes that test didn't reap itself, |
| and report any non-zero exit code as failure. |
| |
| 1.11 Kernel Modules |
| ~~~~~~~~~~~~~~~~~~~ |
| |
| There are certain cases where the test needs a kernel part and userspace part, |
| happily, LTP can build a kernel module and then insert it to the kernel on test |
| start for you. See 'testcases/kernel/device-drivers/block' for details. |
| |
| 1.12 Useful macros |
| ~~~~~~~~~~~~~~~~~~ |
| |
| These macros are defined in 'include/tst_common.h'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| ARRAY_SIZE(arr) |
| ------------------------------------------------------------------------------- |
| |
| Returns the size of statically defined array, i.e. |
| '(sizeof(arr) / sizeof(*arr))' |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| LTP_ALIGN(x, a) |
| ------------------------------------------------------------------------------- |
| |
| Aligns the x to be next multiple of a. The a must be power of 2. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| TST_TO_STR(s) /* stringification */ |
| TST_TO_STR_(s) /* macro expansion */ |
| ------------------------------------------------------------------------------- |
| |
| Macros for stringification. |
| |
| 1.13 Filesystem type detection and skiplist |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests are known to fail on certain filesystems (you cannot swap on TMPFS, |
| there are unimplemented 'fcntl()' etc.). |
| |
| If your test needs to be skipped on certain filesystems use the |
| '.skip_filesystems' field in the tst_test structure as follows: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static struct tst_test test = { |
| ... |
| .skip_filesystems = (const char *const []) { |
| "tmpfs", |
| "ramfs", |
| "nfs", |
| NULL |
| }, |
| }; |
| ------------------------------------------------------------------------------- |
| |
| When the '.all_filesystems' flag is set the '.skip_filesystems' list is passed |
| to the function that detects supported filesystems any listed filesystem is |
| not included in the resulting list of supported filesystems. |
| |
| If test needs to adjust expectations based on filesystem type it's also |
| possible to detect filesystem type at the runtime. This is preferably used |
| when only subset of the test is not applicable for a given filesystem. |
| |
| NOTE: ext2, ext3 or ext4 in '.skip_filesystems' on tests which does *not* use |
| '.all_filesystems' needs to be defined as 'ext2/ext3/ext4'. The reason |
| is that it is hard to detect used filesystem due to overlapping the functionality. |
| OTOH tests which use '.skip_filesystems' *with* '.all_filesystems' can skip |
| only filesystems which are actually used in '.all_filesystems': ext2, ext3, |
| ext4, xfs, btrfs, vfat, exfat, ntfs, tmpfs (defined in 'fs_type_whitelist[]'). |
| It does not make sense to list other filesystems. |
| |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void run(void) |
| { |
| ... |
| |
| switch ((type = tst_fs_type("."))) { |
| case TST_NFS_MAGIC: |
| case TST_TMPFS_MAGIC: |
| case TST_RAMFS_MAGIC: |
| tst_brk(TCONF, "Subtest not supported on %s", |
| tst_fs_type_name(type)); |
| return; |
| break; |
| } |
| |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| 1.14 Thread-safety in the LTP library |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| It is safe to use library 'tst_res()' function in multi-threaded tests. |
| |
| Only the main thread must return from the 'test()' function to the test |
| library and that must be done only after all threads that may call any library |
| function has been terminated. That especially means that threads that may call |
| 'tst_brk()' must terminate before the execution of the 'test()' function |
| returns to the library. This is usually done by the main thread joining all |
| worker threads at the end of the 'test()' function. Note that the main thread |
| will never get to the library code in a case that 'tst_brk()' was called from |
| one of the threads since it will sleep at least in 'pthread_join()' on the |
| thread that called the 'tst_brk()' till 'exit()' is called by 'tst_brk()'. |
| |
| The test-supplied cleanup function runs *concurrently* to the rest of the |
| threads in a case that cleanup was entered from 'tst_brk()'. Subsequent |
| threads entering 'tst_brk()' must be suspended or terminated at the start of |
| the user supplied cleanup function. It may be necessary to stop or exit |
| the rest of the threads before the test cleans up as well. For example threads |
| that create new files should be stopped before temporary directory is be |
| removed. |
| |
| Following code example shows thread safe cleanup function example using atomic |
| increment as a guard. The library calls its cleanup after the execution returns |
| from the user supplied cleanup and expects that only one thread returns from |
| the user supplied cleanup to the test library. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void cleanup(void) |
| { |
| static int flag; |
| |
| if (tst_atomic_inc(&flag) != 1) |
| pthread_exit(NULL); |
| |
| /* if needed stop the rest of the threads here */ |
| |
| ... |
| |
| /* then do cleanup work */ |
| |
| ... |
| |
| /* only one thread returns to the library */ |
| } |
| ------------------------------------------------------------------------------- |
| |
| |
| 1.15 Testing with a block device |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests needs a block device (inotify tests, syscall 'EROFS' failures, |
| etc.). LTP library contains a code to prepare a testing device. |
| |
| If '.needs_device' flag in the 'struct tst_test' is set the 'tst_device' |
| structure is initialized with a path to a test device and default filesystem |
| to be used. |
| |
| You can also request minimal device size in megabytes by setting |
| '.dev_min_size' the device is guaranteed to have at least the requested size |
| then. |
| |
| If '.format_device' flag is set the device is formatted with a filesystem as |
| well. You can use '.dev_fs_type' to override the default filesystem type if |
| needed and pass additional options to mkfs via '.dev_fs_opts' and |
| '.dev_extra_opts' pointers. Note that '.format_device' implies '.needs_device' |
| there is no need to set both. |
| |
| If '.mount_device' is set, the device is mounted at '.mntpoint' which is used |
| to pass a directory name that will be created and used as mount destination. |
| You can pass additional flags and data to the mount command via '.mnt_flags' |
| and '.mnt_data' pointers. Note that '.mount_device' implies '.needs_device' |
| and '.format_device' so there is no need to set the later two. |
| |
| If '.needs_rofs' is set, read-only filesystem is mounted at '.mntpoint' this |
| one is supposed to be used for 'EROFS' tests. |
| |
| If '.all_filesystems' is set the test function is executed for all supported |
| filesystems. Supported filesystems are detected based on existence of the |
| 'mkfs.$fs' helper and on kernel support to mount it. For each supported |
| filesystem the 'tst_device.fs_type' is set to the currently tested fs type, if |
| '.format_device' is set the device is formatted as well, if '.mount_device' is |
| set it's mounted at '.mntpoint'. Also the test timeout is reset for each |
| execution of the test function. This flag is expected to be used for filesystem |
| related syscalls that are at least partly implemented in the filesystem |
| specific code e.g. 'fallocate()'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| struct tst_device { |
| const char *dev; |
| const char *fs_type; |
| }; |
| |
| extern struct tst_device *tst_device; |
| |
| int tst_umount(const char *path); |
| ------------------------------------------------------------------------------- |
| |
| In case that 'LTP_DEV' is passed to the test in an environment, the library |
| checks that the file exists and that it's a block device, if |
| '.device_min_size' is set the device size is checked as well. If 'LTP_DEV' |
| wasn't set or if size requirements were not met a temporary file is created |
| and attached to a free loop device. |
| |
| If there is no usable device and loop device couldn't be initialized the test |
| exits with 'TCONF'. |
| |
| The 'tst_umount()' function works exactly as 'umount(2)' but retries several |
| times on 'EBUSY'. This is because various desktop daemons (gvfsd-trash is known |
| for that) may be stupid enough to probe all newly mounted filesystem which |
| results in 'umount(2)' failing with 'EBUSY'. |
| |
| IMPORTANT: All testcases should use 'tst_umount()' instead of 'umount(2)' to |
| umount filesystems. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_find_free_loopdev(const char *path, size_t path_len); |
| ------------------------------------------------------------------------------- |
| |
| This function finds a free loopdev and returns the free loopdev minor (-1 for no |
| free loopdev). If path is non-NULL, it will be filled with free loopdev path. |
| If you want to use a customized loop device, we can call 'tst_find_free_loopdev(NULL, 0)' |
| in tests to get a free minor number and then mknod. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| unsigned long tst_dev_bytes_written(const char *dev); |
| ------------------------------------------------------------------------------- |
| |
| This function reads test block device stat file ('/sys/block/<device>/stat') and |
| returns the bytes written since the last invocation of this function. To avoid |
| FS deferred IO metadata/cache interference, we suggest doing "syncfs" before the |
| tst_dev_bytes_written first invocation. And an inline function named 'tst_dev_sync()' |
| is created for that intention. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| void tst_find_backing_dev(const char *path, char *dev, size_t dev_size); |
| ------------------------------------------------------------------------------- |
| |
| This function finds the block dev that this path belongs to, using uevent in sysfs. |
| For Btrfs it uses '/sys/fs/btrfs/UUID/devices/DEV_NAME/uevent'; for other |
| filesystems it uses '/sys/dev/block/MAJOR:MINOR/uevent'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| uint64_t tst_get_device_size(const char *dev_path); |
| ------------------------------------------------------------------------------- |
| |
| This function gets size of the given block device, it checks the 'dev_path' is |
| valid first, if yes, return the size in MB, otherwise return -1. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_dev_block_size(const char *path); |
| ------------------------------------------------------------------------------- |
| |
| This function returns the physical device block size for the specific `path`. |
| It finds the device where `path` is located and then uses `ioctl` (BLKSSZGET) |
| to get a physical device block size. |
| |
| 1.16 Formatting a device with a filesystem |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void setup(void) |
| { |
| ... |
| SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL); |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| This function takes a path to a device, filesystem type and an array of extra |
| options passed to mkfs. |
| |
| The fs options 'fs_opts' should either be 'NULL' if there are none, or a |
| 'NULL' terminated array of strings such as: |
| +const char *const opts[] = {"-b", "1024", NULL}+. |
| |
| The extra options 'extra_opts' should either be 'NULL' if there are none, or a |
| 'NULL' terminated array of strings such as +{"102400", NULL}+; 'extra_opts' |
| will be passed after device name. e.g: +mkfs -t ext4 -b 1024 /dev/sda1 102400+ |
| in this case. |
| |
| Note that perfer to store the options which can be passed before or after device |
| name by 'fs_opts' array. |
| |
| 1.17 Verifying a filesystem's free space |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests have size requirements for the filesystem's free space. If these |
| requirements are not satisfied, the tests should be skipped. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_fs_has_free(const char *path, unsigned int size, unsigned int mult); |
| ------------------------------------------------------------------------------- |
| |
| The 'tst_fs_has_free()' function returns 1 if there is enough space and 0 if |
| there is not. |
| |
| The 'path' is the pathname of any directory/file within a filesystem. |
| |
| The 'mult' is a multiplier, one of 'TST_BYTES', 'TST_KB', 'TST_MB' or 'TST_GB'. |
| |
| The required free space is calculated by 'size * mult', e.g. |
| 'tst_fs_has_free("/tmp/testfile", 64, TST_MB)' will return 1 if the |
| filesystem, which '"/tmp/testfile"' is in, has 64MB free space at least, and 0 |
| if not. |
| |
| 1.18 Files, directories and fs limits |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests need to know the maximum count of links to a regular file or |
| directory, such as 'rename(2)' or 'linkat(2)' to test 'EMLINK' error. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_fs_fill_hardlinks(const char *dir); |
| ------------------------------------------------------------------------------- |
| |
| Try to get maximum count of hard links to a regular file inside the 'dir'. |
| |
| NOTE: This number depends on the filesystem 'dir' is on. |
| |
| This function uses 'link(2)' to create hard links to a single file until it |
| gets 'EMLINK' or creates 65535 links. If the limit is hit, the maximum number of |
| hardlinks is returned and the 'dir' is filled with hardlinks in format |
| "testfile%i", where i belongs to [0, limit) interval. If no limit is hit or if |
| 'link(2)' failed with 'ENOSPC' or 'EDQUOT', zero is returned and previously |
| created files are removed. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_fs_fill_subdirs(const char *dir); |
| ------------------------------------------------------------------------------- |
| |
| Try to get maximum number of subdirectories in directory. |
| |
| NOTE: This number depends on the filesystem 'dir' is on. For current kernel, |
| subdir limit is not available for all filesystems (available for ext2, ext3, |
| minix, sysv and more). If the test runs on some other filesystems, like ramfs, |
| tmpfs, it will not even try to reach the limit and return 0. |
| |
| This function uses 'mkdir(2)' to create directories in 'dir' until it gets |
| 'EMLINK' or creates 65535 directories. If the limit is hit, the maximum number |
| of subdirectories is returned and the 'dir' is filled with subdirectories in |
| format "testdir%i", where i belongs to [0, limit - 2) interval (because each |
| newly created dir has two links already - the '.' and the link from parent |
| dir). If no limit is hit or if 'mkdir(2)' failed with 'ENOSPC' or 'EDQUOT', |
| zero is returned and previously created directories are removed. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_dir_is_empty(const char *dir, int verbose); |
| ------------------------------------------------------------------------------- |
| |
| Returns non-zero if directory is empty and zero otherwise. |
| |
| Directory is considered empty if it contains only '.' and '..'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| void tst_purge_dir(const char *path); |
| ------------------------------------------------------------------------------- |
| |
| Deletes the contents of given directory but keeps the directory itself. Useful |
| for cleaning up the temporary directory and mount points between test cases or |
| test iterations. Terminates the program with 'TBROK' on error. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_fill_fd(int fd, char pattern, size_t bs, size_t bcount); |
| ------------------------------------------------------------------------------- |
| |
| Fill a file with specified pattern using file descriptor. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_prealloc_size_fd(int fd, size_t bs, size_t bcount); |
| ------------------------------------------------------------------------------- |
| |
| Preallocate the specified amount of space using 'fallocate()'. Falls back to |
| 'tst_fill_fd()' if 'fallocate()' fails. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_fill_file(const char *path, char pattern, size_t bs, size_t bcount); |
| ------------------------------------------------------------------------------- |
| |
| Creates/overwrites a file with specified pattern using file path. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_prealloc_file(const char *path, size_t bs, size_t bcount); |
| ------------------------------------------------------------------------------- |
| |
| Create/overwrite a file and preallocate the specified amount of space for it. |
| The allocated space will not be initialized to any particular content. |
| |
| 1.19 Getting an unused PID number |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests require a 'PID', which is not used by the OS (does not belong to |
| any process within it). For example, kill(2) should set errno to 'ESRCH' if |
| it's passed such 'PID'. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| pid_t tst_get_unused_pid(void); |
| ------------------------------------------------------------------------------- |
| |
| Return a 'PID' value not used by the OS or any process within it. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_get_free_pids(void); |
| ------------------------------------------------------------------------------- |
| |
| Returns number of unused pids in the system. Note that this number may be |
| different once the call returns and should be used only for rough estimates. |
| |
| 1.20 Running executables |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| int tst_cmd(const char *const argv[], |
| const char *stdout_path, |
| const char *stderr_path, |
| enum tst_cmd_flags flags); |
| ------------------------------------------------------------------------------- |
| |
| 'tst_cmd()' is a wrapper for 'vfork() + execvp()' which provides a way |
| to execute an external program. |
| |
| 'argv[]' is a 'NULL' terminated array of strings starting with the program name |
| which is followed by optional arguments. |
| |
| 'TST_CMD_PASS_RETVAL' enum 'tst_cmd_flags' makes 'tst_cmd()' |
| return the program exit code to the caller, otherwise 'tst_cmd()' exit the |
| tests on failure. 'TST_CMD_TCONF_ON_MISSING' check for program in '$PATH' and exit |
| with 'TCONF' if not found. |
| |
| In case that 'execvp()' has failed and the enum 'TST_CMD_PASS_RETVAL' flag was set, the |
| return value is '255' if 'execvp()' failed with 'ENOENT' and '254' otherwise. |
| |
| 'stdout_path' and 'stderr_path' determine where to redirect the program |
| stdout and stderr I/O streams. |
| |
| The 'SAFE_CMD()' macro can be used automatic handling non-zero exits (exits |
| with 'TBROK') and 'ENOENT' (exits with 'TCONF'). |
| |
| .Example |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| const char *const cmd[] = { "ls", "-l", NULL }; |
| |
| ... |
| /* Store output of 'ls -l' into log.txt */ |
| tst_cmd(cmd, "log.txt", NULL, 0); |
| ... |
| ------------------------------------------------------------------------------- |
| |
| 1.21 Measuring elapsed time and helper functions |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_timer.h" |
| |
| void tst_timer_check(clockid_t clk_id); |
| |
| void tst_timer_start(clockid_t clk_id); |
| |
| void tst_timer_stop(void); |
| |
| struct timespec tst_timer_elapsed(void); |
| |
| long long tst_timer_elapsed_ms(void); |
| |
| long long tst_timer_elapsed_us(void); |
| |
| int tst_timer_expired_ms(long long ms); |
| ------------------------------------------------------------------------------- |
| |
| The 'tst_timer_check()' function checks if specified 'clk_id' is supported and |
| exits the test with 'TCONF' otherwise. It's expected to be used in test |
| 'setup()' before any resources that needs to be cleaned up are initialized, |
| hence it does not include a cleanup function parameter. |
| |
| The 'tst_timer_start()' marks start time and stores the 'clk_id' for further |
| use. |
| |
| The 'tst_timer_stop()' marks the stop time using the same 'clk_id' as last |
| call to 'tst_timer_start()'. |
| |
| The 'tst_timer_elapsed*()' returns time difference between the timer start and |
| last timer stop in several formats and units. |
| |
| The 'tst_timer_expired_ms()' function checks if the timer started by |
| 'tst_timer_start()' has been running longer than ms milliseconds. The function |
| returns non-zero if timer has expired and zero otherwise. |
| |
| IMPORTANT: The timer functions use 'clock_gettime()' internally which needs to |
| be linked with '-lrt' on older glibc. Please do not forget to add |
| 'LDLIBS+=-lrt' in Makefile. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| #include "tst_timer.h" |
| |
| static void setup(void) |
| { |
| ... |
| tst_timer_check(CLOCK_MONOTONIC); |
| ... |
| } |
| |
| static void run(void) |
| { |
| ... |
| tst_timer_start(CLOCK_MONOTONIC); |
| ... |
| while (!tst_timer_expired_ms(5000)) { |
| ... |
| } |
| ... |
| } |
| |
| struct tst_test test = { |
| ... |
| .setup = setup, |
| .test_all = run, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Expiration timer example usage. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| long long tst_timespec_to_us(struct timespec t); |
| long long tst_timespec_to_ms(struct timespec t); |
| |
| struct timeval tst_us_to_timeval(long long us); |
| struct timeval tst_ms_to_timeval(long long ms); |
| |
| int tst_timespec_lt(struct timespec t1, struct timespec t2); |
| |
| struct timespec tst_timespec_add_us(struct timespec t, long long us); |
| |
| struct timespec tst_timespec_diff(struct timespec t1, struct timespec t2); |
| long long tst_timespec_diff_us(struct timespec t1, struct timespec t2); |
| long long tst_timespec_diff_ms(struct timespec t1, struct timespec t2); |
| |
| struct timespec tst_timespec_abs_diff(struct timespec t1, struct timespec t2); |
| long long tst_timespec_abs_diff_us(struct timespec t1, struct timespec t2); |
| long long tst_timespec_abs_diff_ms(struct timespec t1, struct timespec t2); |
| ------------------------------------------------------------------------------- |
| |
| The first four functions are simple inline conversion functions. |
| |
| The 'tst_timespec_lt()' function returns non-zero if 't1' is earlier than |
| 't2'. |
| |
| The 'tst_timespec_add_us()' function adds 'us' microseconds to the timespec |
| 't'. The 'us' is expected to be positive. |
| |
| The 'tst_timespec_diff*()' functions returns difference between two times, the |
| 't1' is expected to be later than 't2'. |
| |
| The 'tst_timespec_abs_diff*()' functions returns absolute value of difference |
| between two times. |
| |
| NOTE: All conversions to ms and us rounds the value. |
| |
| 1.22 Datafiles |
| ~~~~~~~~~~~~~~ |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static const char *const res_files[] = { |
| "foo", |
| "bar", |
| NULL |
| }; |
| |
| static struct tst_test test = { |
| ... |
| .resource_files = res_files, |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| If the test needs additional files to be copied to the test temporary |
| directory all you need to do is to list their filenames in the |
| 'NULL' terminated array '.resource_files' in the tst_test structure. |
| |
| When resource files is set test temporary directory is created automatically, |
| there is need to set '.needs_tmpdir' as well. |
| |
| The test library looks for datafiles first, these are either stored in a |
| directory called +datafiles+ in the +$PWD+ at the start of the test or in |
| +$LTPROOT/testcases/data/${test_binary_name}+. If the file is not found the |
| library looks into +$LTPROOT/testcases/bin/+ and to +$PWD+ at the start of the |
| test. This ensures that the testcases can copy the file(s) effortlessly both |
| when test is started from the directory it was compiled in as well as when LTP |
| was installed. |
| |
| The file(s) are copied to the newly created test temporary directory which is |
| set as the test working directory when the 'test()' functions is executed. |
| |
| 1.23 Code path tracing |
| ~~~~~~~~~~~~~~~~~~~~~~ |
| |
| 'tst_res' is a macro, so on when you define a function in one file: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| int do_action(int arg) |
| { |
| ... |
| |
| if (ok) { |
| tst_res(TPASS, "check passed"); |
| return 0; |
| } else { |
| tst_res(TFAIL, "check failed"); |
| return -1; |
| } |
| } |
| ------------------------------------------------------------------------------- |
| |
| and call it from another file, the file and line reported by 'tst_res' in this |
| function will be from the former file. |
| |
| 'TST_TRACE' can make the analysis of such situations easier. It's a macro which |
| inserts a call to 'tst_res(TINFO, ...)' in case its argument evaluates to |
| non-zero. In this call to 'tst_res(TINFO, ...)' the file and line will be |
| expanded using the actual location of 'TST_TRACE'. |
| |
| For example, if this another file contains: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| if (TST_TRACE(do_action(arg))) { |
| ... |
| } |
| ------------------------------------------------------------------------------- |
| |
| the generated output may look similar to: |
| |
| ------------------------------------------------------------------------------- |
| common.h:9: FAIL: check failed |
| test.c:8: INFO: do_action(arg) failed |
| ------------------------------------------------------------------------------- |
| |
| 1.24 Tainted kernels |
| ~~~~~~~~~~~~~~~~~~~~ |
| |
| If you need to detect whether a testcase triggers a kernel warning, bug or |
| oops, the following can be used to detect TAINT_W or TAINT_D: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static struct tst_test test = { |
| ... |
| .taint_check = TST_TAINT_W | TST_TAINT_D, |
| ... |
| }; |
| |
| void run(void) |
| { |
| ... |
| if (tst_taint_check() != 0) |
| tst_res(TFAIL, "kernel has issues"); |
| else |
| tst_res(TPASS, "kernel seems to be fine"); |
| } |
| ------------------------------------------------------------------------------- |
| |
| To initialize taint checks, you have to set the taint flags you want to test |
| for in the 'taint_check' attribute of the tst_test struct. LTP library will |
| then automatically call 'tst_taint_init()' during test setup. The function |
| will generate a 'TCONF' if the requested flags are not fully supported on the |
| running kernel, and 'TBROK' if the kernel is already tainted before executing |
| the test. |
| |
| LTP library will then automatically check kernel taint at the end of testing. |
| If '.all_filesystems' is set in struct tst_test, taint check will be performed |
| after each file system and taint will abort testing early with 'TFAIL'. You |
| can optionally also call 'tst_taint_check()' during 'run()', which returns 0 |
| or the tainted flags set in '/proc/sys/kernel/tainted' as specified earlier. |
| |
| Depending on your kernel version, not all tainted-flags will be supported. |
| |
| For reference to tainted kernels, see kernel documentation: |
| Documentation/admin-guide/tainted-kernels.rst or |
| https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html |
| |
| 1.25 Checksums |
| ~~~~~~~~~~~~~~ |
| |
| CRC32c checksum generation is supported by LTP. In order to use it, the |
| test should include 'tst_checksum.h' header, then can call 'tst_crc32c()'. |
| |
| 1.26 Checking kernel for the driver support |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests may need specific kernel drivers, either compiled in, or built |
| as a module. If '.needs_drivers' points to a 'NULL' terminated array of kernel |
| module names these are all checked and the test exits with 'TCONF' on the |
| first missing driver. |
| |
| The detection is based on reading 'modules.dep' and 'modules.builtin' files |
| generated by kmod. The check is skipped on Android. |
| |
| 1.27 Saving & restoring /proc|sys values |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| LTP library can be instructed to save and restore value of specified |
| (/proc|sys) files. This is achieved by initialized tst_test struct |
| field 'save_restore'. It is a NULL-terminated array of struct |
| 'tst_path_val' where each tst_path_val.path represents a file, whose |
| value is saved at the beginning and restored at the end of the test. |
| If non-NULL string is passed in tst_path_val.val, it is written |
| to the respective file at the beginning of the test. Only the first line |
| of a specified file is saved and restored. |
| |
| By default, the test will end with TCONF if the file is read-only or |
| does not exist. If the optional write of new value fails, the test will end |
| with 'TBROK'. This behavior can be changed using tst_path_val.flags: |
| |
| * 'TST_SR_TBROK_MISSING' – End test with 'TBROK' if the file does not exist |
| * 'TST_SR_TCONF_MISSING' – End test with 'TCONF' if the file does not exist |
| * 'TST_SR_SKIP_MISSING' – Continue without saving the file if it does not exist |
| * 'TST_SR_TBROK_RO' – End test with 'TBROK' if the file is read-only |
| * 'TST_SR_TCONF_RO' – End test with 'TCONF' if the file is read-only |
| * 'TST_SR_SKIP_RO' – Continue without saving the file if it is read-only |
| * 'TST_SR_IGNORE_ERR' – Ignore errors when writing new value into the file |
| |
| Common flag combinations also have shortcuts: |
| |
| * 'TST_SR_TCONF' – Equivalent to 'TST_SR_TCONF_MISSING | TST_SR_TCONF_RO' |
| * 'TST_SR_TBROK' – Equivalent to 'TST_SR_TBROK_MISSING | TST_SR_TBROK_RO' |
| * 'TST_SR_SKIP' – Equivalent to 'TST_SR_SKIP_MISSING | TST_SR_SKIP_RO' |
| |
| 'restore' is always strict and will TWARN if it encounters any error. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static struct tst_test test = { |
| ... |
| .setup = setup, |
| .save_restore = (const struct tst_path_val[]) { |
| {"/proc/sys/kernel/core_pattern", NULL, TST_SR_TCONF}, |
| {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP}, |
| {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK}, |
| {} |
| }, |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.28 Parsing kernel .config |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Generally testcases should attempt to autodetect as much kernel features as |
| possible based on the currently running kernel. We do have tst_check_driver() |
| to check if functionality that could be compiled as kernel module is present |
| on the system, disabled syscalls can be detected by checking for 'ENOSYS' |
| errno etc. |
| |
| However in rare cases core kernel features couldn't be detected based on the |
| kernel userspace API and we have to resort to parse the kernel .config. |
| |
| For this cases the test should set the 'NULL' terminated '.needs_kconfigs' |
| array of boolean expressions with constraints on the kconfig variables. The |
| boolean expression consits of variables, two binary operations '&' and '|', |
| negation '!' and correct sequence of parentesis '()'. Variables are expected |
| to be in a form of "CONFIG_FOO[=bar]". |
| |
| The test will continue to run if all expressions are evaluated to 'True'. |
| Missing variable is mapped to 'False' as well as variable with different than |
| specified value, e.g. 'CONFIG_FOO=bar' will evaluate to 'False' if the value |
| is anything else but 'bar'. If config variable is specified as plain |
| 'CONFIG_FOO' it's evaluated to true it's set to any value (typically =y or =m). |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static const char *kconfigs[] = { |
| "CONFIG_X86_INTEL_UMIP | CONFIG_X86_UMIP", |
| NULL |
| }; |
| |
| static struct tst_test test = { |
| ... |
| .needs_kconfigs = kconfigs, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.29 Changing the Wall Clock Time during test execution |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| There are some tests that, for different reasons, might need to change the |
| system-wide clock time. Whenever this happens, it is imperative that the clock |
| is restored, at the end of test's execution, taking in consideration the amount |
| of time elapsed during that test. |
| |
| In order for that to happen, struct tst_test has a variable called |
| "restore_wallclock" that should be set to "1" so LTP knows it should: (1) |
| initialize a monotonic clock during test setup phase and (2) use that monotonic |
| clock to fix the system-wide clock time at the test cleanup phase. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void setup(void) |
| { |
| ... |
| } |
| |
| static void run(void) |
| { |
| ... |
| } |
| |
| struct tst_test test = { |
| ... |
| .setup = setup, |
| .test_all = run, |
| .restore_wallclock = 1, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.30 Testing similar syscalls in one test |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| In some cases kernel has several very similar syscalls that do either the same |
| or very similar job. This is most noticeable on i386 where we commonly have |
| two or three syscall versions. That is because i386 was first platform that |
| Linux was developed on and because of that most mistakes in API happened there |
| as well. However this is not limited to i386 at all, it's quite common that |
| version two syscall has added missing flags parameters or so. |
| |
| In such cases it does not make much sense to copy&paste the test code over and |
| over, rather than that the test library provides support for test variants. |
| The idea behind test variants is simple, we run the test several times each |
| time with different syscall variant. |
| |
| The implementation consist of test_variants integer that, if set, denotes number |
| of test variants. The test is then forked and executed test_variants times each |
| time with different value in global tst_variant variable. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static int do_foo(void) |
| { |
| switch (tst_variant) { |
| case 0: |
| return foo(); |
| case 1: |
| return syscall(__NR_foo); |
| } |
| |
| return -1; |
| } |
| |
| static void run(void) |
| { |
| ... |
| |
| TEST(do_foo); |
| |
| ... |
| } |
| |
| static void setup(void) |
| { |
| switch (tst_variant) { |
| case 0: |
| tst_res(TINFO, "Testing foo variant 1"); |
| break; |
| case 1: |
| tst_res(TINFO, "Testing foo variant 2"); |
| break; |
| } |
| } |
| |
| struct tst_test test = { |
| ... |
| .setup = setup, |
| .test_all = run, |
| .test_variants = 2, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.31 Guarded buffers |
| ~~~~~~~~~~~~~~~~~~~~ |
| |
| The test library supports guarded buffers, which are buffers allocated so |
| that: |
| |
| * The end of the buffer is followed by a PROT_NONE page |
| |
| * The remainder of the page before the buffer is filled with random canary |
| data |
| |
| Which means that the any access after the buffer will yield a Segmentation |
| fault or EFAULT depending on if the access happened in userspace or the kernel |
| respectively. The canary before the buffer will also catch any write access |
| outside of the buffer. |
| |
| The purpose of the patch is to catch off-by-one bugs which happens when |
| buffers and structures are passed to syscalls. New tests should allocate |
| guarded buffers for all data passed to the tested syscall which are passed by |
| a pointer. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static struct foo *foo_ptr; |
| static struct iovec *iov; |
| static void *buf_ptr; |
| static char *id; |
| ... |
| |
| static void run(void) |
| { |
| ... |
| |
| foo_ptr->bar = 1; |
| foo_ptr->buf = buf_ptr; |
| |
| ... |
| } |
| |
| static void setup(void) |
| { |
| ... |
| |
| id = tst_strdup(string); |
| |
| ... |
| } |
| |
| static struct tst_test test = { |
| ... |
| .bufs = (struct tst_buffers []) { |
| {&foo_ptr, .size = sizeof(*foo_ptr)}, |
| {&buf_ptr, .size = BUF_SIZE}, |
| {&iov, .iov_sizes = (int[]){128, 32, -1}, |
| {} |
| } |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Guarded buffers can be allocated on runtime in a test setup() by a |
| 'tst_alloc()' or by 'tst_strdup()' as well as by filling up the .bufs array in |
| the tst_test structure. |
| |
| So far the tst_test structure supports allocating either a plain buffer by |
| setting up the size or struct iovec, which is allocated recursively including |
| the individual buffers as described by an '-1' terminated array of buffer |
| sizes. |
| |
| 1.32 Adding and removing capabilities |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests may require the presence or absence of particular |
| capabilities. Using the API provided by 'tst_capability.h' the test author can |
| try to ensure that some capabilities are either present or absent during the |
| test. |
| |
| For example; below we try to create a raw socket, which requires |
| CAP_NET_ADMIN. During setup we should be able to do it, then during run it |
| should be impossible. The LTP capability library will check before setup that |
| we have this capability, then after setup it will drop it. |
| |
| [source,c] |
| -------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| #include "tst_capability.h" |
| #include "tst_safe_net.h" |
| |
| #include "lapi/socket.h" |
| |
| static void run(void) |
| { |
| TEST(socket(AF_INET, SOCK_RAW, 1)); |
| if (TST_RET > -1) { |
| tst_res(TFAIL, "Created raw socket"); |
| } else if (TST_ERR != EPERM) { |
| tst_res(TFAIL | TTERRNO, |
| "Failed to create socket for wrong reason"); |
| } else { |
| tst_res(TPASS | TTERRNO, "Didn't create raw socket"); |
| } |
| } |
| |
| static void setup(void) |
| { |
| TEST(socket(AF_INET, SOCK_RAW, 1)); |
| if (TST_RET < 0) |
| tst_brk(TCONF | TTERRNO, "We don't have CAP_NET_RAW to begin with"); |
| |
| SAFE_CLOSE(TST_RET); |
| } |
| |
| static struct tst_test test = { |
| .setup = setup, |
| .test_all = run, |
| .caps = (struct tst_cap []) { |
| TST_CAP(TST_CAP_REQ, CAP_NET_RAW), |
| TST_CAP(TST_CAP_DROP, CAP_NET_RAW), |
| {} |
| }, |
| }; |
| -------------------------------------------------------------------------------- |
| |
| Look at the test struct at the bottom. We have filled in the 'caps' field with |
| a 'NULL' terminated array containing two 'tst_cap' structs. 'TST_CAP_REQ' |
| actions are executed before setup and 'TST_CAP_DROP' are executed after |
| setup. This means it is possible to both request and drop a capability. |
| |
| [source,c] |
| -------------------------------------------------------------------------------- |
| static struct tst_test test = { |
| .test_all = run, |
| .caps = (struct tst_cap []) { |
| TST_CAP(TST_CAP_REQ, CAP_NET_RAW), |
| TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), |
| {} |
| }, |
| }; |
| -------------------------------------------------------------------------------- |
| |
| Here we request 'CAP_NET_RAW', but drop 'CAP_SYS_ADMIN'. If the capability is |
| in the permitted set, but not the effective set, the library will try to |
| permit it. If it is not in the permitted set, then it will fail with 'TCONF'. |
| |
| This API does not require 'libcap' to be installed. However it has limited |
| features relative to 'libcap'. It only tries to add or remove capabilities |
| from the effective set. This means that tests which need to spawn child |
| processes may have difficulties ensuring the correct capabilities are |
| available to the children (see the capabilities (7) manual pages). |
| |
| However a lot of problems can be solved by using 'tst_cap_action(struct |
| tst_cap *cap)' directly which can be called at any time. This also helps if |
| you wish to drop a capability at the beginning of setup. |
| |
| 1.33 Reproducing race-conditions |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| If a bug is caused by two tasks in the kernel racing and you wish to create a |
| regression test (or bug-fix validation test) then the 'tst_fuzzy_sync.h' |
| library should be used. |
| |
| It allows you to specify, in your code, two race windows. One window in each |
| thread's loop (triggering a race usually requires many iterations). These |
| windows show fuzzy-sync where the race can happen. They don't need to be |
| exact, hence the 'fuzzy' part. If the race condition is not immediately |
| triggered then the library will begin experimenting with different timings. |
| |
| [source,c] |
| -------------------------------------------------------------------------------- |
| #include "tst_fuzzy_sync.h" |
| |
| static struct tst_fzsync_pair fzsync_pair; |
| |
| static void setup(void) |
| { |
| tst_fzsync_pair_init(&fzsync_pair); |
| } |
| |
| static void cleanup(void) |
| { |
| tst_fzsync_pair_cleanup(&fzsync_pair); |
| } |
| |
| static void *thread_b(void *arg) |
| { |
| while (tst_fzsync_run_b(&fzsync_pair)) { |
| |
| tst_fzsync_start_race_b(&fzsync_pair); |
| |
| /* This is the race window for thread B */ |
| |
| tst_fzsync_end_race_b(&fzsync_pair); |
| } |
| |
| return arg; |
| } |
| |
| static void thread_a(void) |
| { |
| tst_fzsync_pair_reset(&fzsync_pair, thread_b); |
| |
| while (tst_fzsync_run_a(&fzsync_pair)) { |
| |
| tst_fzsync_start_race_a(&fzsync_pair); |
| |
| /* This is the race window for thread A */ |
| |
| tst_fzsync_end_race_a(&fzsync_pair); |
| } |
| } |
| |
| static struct tst_test test = { |
| .test_all = thread_a, |
| .setup = setup, |
| .cleanup = cleanup, |
| }; |
| -------------------------------------------------------------------------------- |
| |
| Above is a minimal template for a test using fuzzy-sync. In a simple case, you |
| just need to put the bits you want to race inbetween 'start_race' and |
| 'end_race'. Meanwhile, any setup you need to do per-iteration goes outside the |
| windows. |
| |
| Fuzzy sync synchronises 'run_a' and 'run_b', which act as barriers, so that |
| neither thread can progress until the other has caught up with it. There is |
| also the 'pair_wait' function which can be used to add barriers in other |
| locations. Of course 'start/end_race_a/b' are also a barriers. |
| |
| The library decides how long the test should run for based on the timeout |
| specified by the user plus some other heuristics. |
| |
| For full documentation see the comments in 'include/tst_fuzzy_sync.h'. |
| |
| 1.34 Reserving hugepages |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Many of the LTP tests need to use hugepage in their testing, this allows the |
| test can reserve hugepages from system via '.hugepages = {xx, TST_REQUEST}'. |
| |
| We achieved two policies for reserving hugepages: |
| |
| TST_REQUEST: |
| It will try the best to reserve available huge pages and return the number |
| of available hugepages in tst_hugepages, which may be 0 if hugepages are |
| not supported at all. |
| |
| TST_NEEDS: |
| This is an enforced requirement, LTP should strictly do hpages applying and |
| guarantee the 'HugePages_Free' no less than pages which makes that test can |
| use these specified numbers correctly. Otherwise, test exits with TCONF if |
| the attempt to reserve hugepages fails or reserves less than requested. |
| |
| With success test stores the reserved hugepage number in 'tst_hugepages'. For |
| system without hugetlb supporting, variable 'tst_hugepages' will be set to 0. |
| If the hugepage number needs to be set to 0 on supported hugetlb system, please |
| use '.hugepages = {TST_NO_HUGEPAGES}'. |
| |
| Also, we do cleanup and restore work for the hpages resetting automatically. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void run(void) |
| { |
| ... |
| |
| if (tst_hugepages == test.hugepages.number) |
| TEST(do_hpage_test); |
| else |
| ... |
| ... |
| } |
| |
| struct tst_test test = { |
| .test_all = run, |
| .hugepages = {2, TST_REQUEST}, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| or, |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void run(void) |
| { |
| ... |
| } |
| |
| static void setup(void) |
| { |
| /* TST_NEEDS achieved this automatically in the library */ |
| if (tst_hugepages != test.hugepages.number) |
| tst_brk(TCONF, "..."); |
| } |
| |
| struct tst_test test = { |
| .test_all = run, |
| .hugepages = {2, TST_NEEDS}, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.35 Checking for required commands |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Required commands can be checked with '.needs_cmds', which points to a 'NULL' |
| terminated array of strings such as: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| .needs_cmds = (const char *const []) { |
| "useradd", |
| "userdel", |
| NULL |
| }, |
| ------------------------------------------------------------------------------- |
| |
| Also can check required command version whether is satisfied by using 'needs_cmds' |
| such as: |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| .needs_cmds = (const char *const []) { |
| "mkfs.ext4 >= 1.43.0", |
| NULL |
| }, |
| ------------------------------------------------------------------------------- |
| |
| Currently, we only support mkfs.ext4 command version check. |
| If you want to support more commands, please fill your own .parser and .table_get |
| method in the version_parsers structure of lib/tst_cmd.c. |
| |
| 1.36 Assert sys or proc file value |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| Using TST_ASSERT_INT/STR(path, val) to assert that integer value or string stored in |
| the prefix field of file pointed by path equals to the value passed to this function. |
| |
| Also having a similar api pair TST_ASSERT_FILE_INT/STR(path, prefix, val) to assert |
| the field value of file. |
| |
| 1.37 Using Control Group |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some LTP tests need specific Control Group configurations. 'tst_cgroup.h' |
| provides APIs to discover and use CGroups. There are many differences between |
| CGroups API V1 and V2. We encapsulate the details of configuring CGroups in |
| high-level functions which follow the V2 kernel API where possible. Allowing one |
| to write code that works on both V1 or V2. At least some of the time anyway; |
| often the behavioural differences between V1 and V2 are too great. In such cases |
| we revert to branching on the CGroup version. |
| |
| Also, the LTP library will automatically mount/umount and configure the CGroup |
| hierarchies if that is required (e.g. if you run the tests from init with no |
| system manager). |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static void run(void) |
| { |
| ... |
| // do test under cgroup |
| ... |
| } |
| |
| static void setup(void) |
| { |
| SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); |
| SAFE_CG_PRINTF(tst_cg, "memory.max", "%lu", MEMSIZE); |
| if (SAFE_CG_HAS(tst_cg, "memory.swap.max")) |
| SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%zu", memsw); |
| } |
| |
| struct tst_test test = { |
| .setup = setup, |
| .test_all = run, |
| .cleanup = cleanup, |
| .needs_cgroup_ctrls = (const char *const []){ "memory", NULL }, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Above, we first ensure the memory controller is available on the |
| test's CGroup with '.needs_cgroup_ctrls'. This populates a structure, |
| 'tst_cg', which represents the test's CGroup. |
| |
| We then write the current processes PID into 'cgroup.procs', which |
| moves the current process into the test's CGroup. After which we set |
| the maximum memory size by writing to 'memory.max'. If the memory |
| controller is mounted on CGroups V1 then the library will actually |
| write to 'memory.limit_in_bytes'. As a general rule, if a file exists |
| on both CGroup versions, then we use the V2 naming. |
| |
| Some controller features, such as 'memory.swap', can be |
| disabled. Therefor we need to check if they exist before accessing |
| them. This can be done with 'SAFE_CG_HAS' which can be called on |
| any control file or feature. |
| |
| Most tests only require setting a few limits similar to the above. In |
| such cases the differences between V1 and V2 are hidden. Setup and |
| cleanup is also mostly hidden. However things can get much worse. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| static struct tst_cg_group *cg_child; |
| |
| static void run(void) |
| { |
| char buf[BUFSIZ]; |
| size_t mem = 0; |
| |
| cg_child = tst_cg_group_mk(tst_cg, "child"); |
| SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid()); |
| |
| if (!TST_CG_VER_IS_V1(tst_cg, "memory")) |
| SAFE_CG_PRINT(tst_cg, "cgroup.subtree_control", "+memory"); |
| if (!TST_CG_VER_IS_V1(tst_cg, "cpuset")) |
| SAFE_CG_PRINT(tst_cg, "cgroup.subtree_control", "+cpuset"); |
| |
| if (!SAFE_FORK()) { |
| SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid()); |
| |
| if (SAFE_CG_HAS(cg_child, "memory.swap")) { |
| SAFE_CG_SCANF(cg_child, |
| "memory.swap.current", "%zu", &mem); |
| } |
| SAFE_CG_READ(cg_child, "cpuset.mems", buf, sizeof(buf)); |
| |
| // Do something with cpuset.mems and memory.current values |
| ... |
| |
| exit(0); |
| } |
| |
| tst_reap_children(); |
| SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid()); |
| cg_child = tst_cg_group_rm(cg_child); |
| } |
| |
| static void cleanup(void) |
| { |
| if (cg_child) { |
| SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid()); |
| cg_child = tst_cg_group_rm(cg_child); |
| } |
| } |
| |
| struct tst_test test = { |
| .setup = setup, |
| .test_all = run, |
| .needs_cgroup_ctrls = (const char *const []){ |
| "cpuset", |
| "memory", |
| NULL |
| }, |
| ... |
| }; |
| ------------------------------------------------------------------------------- |
| |
| Starting with setup; we can see here that we fetch the 'drain' |
| CGroup. This is a shared group (between parallel tests) which may |
| contain processes from other tests. It should have default settings |
| and these should not be changed by the test. It can be used to remove |
| processes from other CGroups incase the hierarchy root is not |
| accessible. |
| |
| Note that 'tst_cg_get_drain_group' should not be called many times, |
| as it is allocated in a guarded buffer (See section 2.2.31). Therefor |
| it is best to call it once in 'setup' and not 'run' because 'run' may |
| be repeated with the '-i' option. |
| |
| In 'run', we first create a child CGroup with 'tst_cg_mk'. As we |
| create this CGroup in 'run' we should also remove it at the end of |
| run. We also need to check if it exists and remove it in cleanup as |
| well. Because there are 'SAFE_' functions which may jump to cleanup. |
| |
| We then move the main test process into the child CGroup. This is |
| important as it means that before we destroy the child CGroup we have |
| to move the main test process elsewhere. For that we use the 'drain' |
| group. |
| |
| Next we enable the memory and cpuset controller configuration on the |
| test CGroup's descendants (i.e. 'cg_child'). This allows each child to |
| have its own settings. The file 'cgroup.subtree_control' does not |
| exist on V1. Because it is possible to have both V1 and V2 active at |
| the same time. We can not simply check if 'subtree_control' exists |
| before writing to it. We have to check if a particular controller is |
| on V2 before trying to add it to 'subtree_control'. Trying to add a V1 |
| controller will result in 'ENOENT'. |
| |
| We then fork a child process and add this to the child CGroup. Within |
| the child process we try to read 'memory.swap.current'. It is possible |
| that the memory controller was compiled without swap support, so it is |
| necessary to check if 'memory.swap' is enabled. That is unless the |
| test will never reach the point where 'memory.swap.*' are used without |
| swap support. |
| |
| The parent process waits for the child process to be reaped before |
| destroying the child CGroup. So there is no need to transfer the child |
| to drain. However the parent process must be moved otherwise we will |
| get 'EBUSY' when trying to remove the child CGroup. |
| |
| Another example of a behavioral difference between versions is shown below. |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| if (TST_CG_VER_IS_V1(tst_cg, "memory")) |
| SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", ~0UL); |
| else |
| SAFE_CG_PRINT(tst_cg, "memory.swap.max", "max"); |
| ------------------------------------------------------------------------------- |
| |
| CGroups V2 introduced a feature where 'memory[.swap].max' could be set to |
| "max". This does not appear to work on V1 'limit_in_bytes' however. For most |
| tests, simply using a large number is sufficient and there is no need to use |
| "max". Importantly though, one should be careful to read both the V1 and V2 |
| kernel docs. Presently the LTP library does not attempt to handle most |
| differences in semantics. It does the minimal amount of work to make testing on |
| both V1 and V2 feasible. |
| |
| 1.38 Require minimum numbers of CPU for a testcase |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests require more than specific number of CPU. It can be defined with |
| `.min_cpus = N`. |
| |
| 1.39 Require minimum memory or swap size for a testcase |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Some tests require at least size(MB) of free RAM or Swap. |
| |
| To make sure that test will run only on systems with more than minimal |
| required amount of RAM set `.min_mem_avail = N`. |
| |
| Similarily for tests that require certain amount of free Swap use |
| `.min_swap_avail = N`. |
| |
| 1.40 Test tags |
| ~~~~~~~~~~~~~~ |
| |
| Test tags are name-value pairs that can hold any test metadata. |
| |
| We have additional support for CVE entries, git commit in mainline kernel, |
| stable kernel or glibc git repository. If a test is a regression test it |
| should include these tags. They are printed when test fails and exported |
| into documentation. |
| |
| CVE, mainline and stable kernel git commits in a regression test for a kernel bug: |
| [source,c] |
| ------------------------------------------------------------------------------- |
| struct tst_test test = { |
| ... |
| .tags = (const struct tst_tag[]) { |
| {"linux-git", "9392a27d88b9"}, |
| {"linux-git", "ff002b30181d"}, |
| {"known-fail", "ustat() is known to fail with EINVAL on Btrfs"}, |
| {"linux-stable-git", "c4a23c852e80"}, |
| {"CVE", "2020-29373"}, |
| {} |
| } |
| }; |
| ------------------------------------------------------------------------------- |
| |
| NOTE: We don't track all backports to stable kernel but just those which are |
| stable branch specific (unique), i.e. no commit in mainline. Example of |
| commits: c4a23c852e80, cac68d12c531. |
| |
| Glibc and musl git commits in a regression test for glibc and musl bugs: |
| [source,c] |
| ------------------------------------------------------------------------------- |
| struct tst_test test = { |
| ... |
| .tags = (const struct tst_tag[]) { |
| {"glibc-git", "574500a108be"}, |
| {"musl-git", "fa4a8abd06a4"}, |
| {} |
| } |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.41 Testing on the specific architecture |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| Testcases for specific arch should be limited on that only being supported |
| platform to run, we now involve a '.supported_archs' to achieve this feature |
| in LTP library. All you need to run a test on the expected arch is to set |
| the '.supported_archs' array in the 'struct tst_test' to choose the required |
| arch list. e.g. |
| |
| .supported_archs = (const char *const []){"x86_64", "ppc64", NULL} |
| |
| This helps move the TCONF info from code to tst_test metadata as well. |
| |
| And, we also export a struct tst_arch to save the system architecture for |
| using in the whole test cases. |
| |
| extern const struct tst_arch { |
| char name[16]; |
| enum tst_arch_type type; |
| } tst_arch; |
| |
| [source,c] |
| ------------------------------------------------------------------------------- |
| #include "tst_test.h" |
| |
| static struct tst_test test = { |
| ... |
| .setup = setup, |
| .supported_archs = (const char *const []) { |
| "x86_64", |
| "ppc64", |
| "s390x", |
| NULL |
| }, |
| }; |
| ------------------------------------------------------------------------------- |
| |
| 1.42 Skipping test based on system state |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| Test can be skipped on various conditions: on enabled SecureBoot |
| ('.skip_in_secureboot = 1'), lockdown ('.skip_in_lockdown = 1') or in 32-bit |
| compat mode ('.skip_in_compat = 1'). |
| |
| 2. Common problems |
| ------------------ |
| |
| This chapter describes common problems/misuses and less obvious design patters |
| (quirks) in UNIX interfaces. Read it carefully :) |
| |
| 2.1 umask() |
| ~~~~~~~~~~~ |
| |
| I've been hit by this one several times already... When you create files |
| with 'open()' or 'creat()' etc, the mode specified as the last parameter *is |
| not* the mode the file is created with. The mode depends on current 'umask()' |
| settings which may clear some of the bits. If your test depends on specific |
| file permissions you need either to change umask to 0 or 'chmod()' the file |
| afterwards or use 'SAFE_TOUCH()' that does the 'chmod()' for you. |
| |
| 2.2 access() |
| ~~~~~~~~~~~~ |
| |
| If 'access(some_file, W_OK)' is executed by root, it will return success even |
| if the file doesn't have write permission bits set (the same holds for R_OK |
| too). For sysfs files you can use 'open()' as a workaround to check file |
| read/write permissions. It might not work for other filesystems, for these you |
| have to use 'stat()', 'lstat()' or 'fstat()'. |
| |
| 2.3 umount() EBUSY |
| ~~~~~~~~~~~~~~~~~~ |
| |
| Various desktop daemons (gvfsd-trash is known for that) may be stupid enough |
| to probe all newly mounted filesystem which results in 'umount(2)' failing |
| with 'EBUSY'; use 'tst_umount()' described in 1.19 that retries in this case |
| instead of plain 'umount(2)'. |
| |
| 2.4 FILE buffers and fork() |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Be vary that if a process calls 'fork(2)' the child process inherits open |
| descriptors as well as copy of the parent memory so especially if there are |
| any open 'FILE' buffers with a data in them they may be written both by the |
| parent and children resulting in corrupted/duplicated data in the resulting |
| files. |
| |
| Also open 'FILE' streams are flushed and closed at 'exit(3)' so if your |
| program works with 'FILE' streams, does 'fork(2)', and the child may end up |
| calling 'exit(3)' you will likely end up with corrupted files. |
| |
| The solution to this problem is either simply call 'fflush(NULL)' that flushes |
| all open output 'FILE' streams just before doing 'fork(2)'. You may also use |
| '_exit(2)' in child processes which does not flush 'FILE' buffers and also |
| skips 'atexit(3)' callbacks. |