]> granicus.if.org Git - strace/blob - tests/times.c
strace: terminate itself if interrupted by a signal
[strace] / tests / times.c
1 /*
2  * Copyright (c) 2015 Eugene Syromyatnikov <evgsyr@gmail.com>
3  * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org>
4  * Copyright (c) 2015-2017 The strace developers.
5  * All rights reserved.
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9
10 /**
11  * @file
12  * This test burns some CPU cycles in user space and kernel space in order to
13  * get some non-zero values returned by times(2).
14  */
15
16 #include "tests.h"
17 #include <sched.h>
18 #include <stdio.h>
19 #include <time.h>
20 #include <unistd.h>
21
22 #include <asm/unistd.h>
23 #include <sys/times.h>
24 #include <sys/wait.h>
25
26 enum {
27         NUM_USER_ITERS = 1000000,
28         PARENT_CPUTIME_LIMIT_NSEC = 200000000,
29         CHILD_CPUTIME_LIMIT_NSEC = 300000000
30 };
31
32 int
33 main(void)
34 {
35         struct timespec ts;
36         volatile int dummy = 0;
37         int i = 0;
38
39         pid_t pid = fork();
40         if (pid < 0)
41                 perror_msg_and_fail("fork");
42
43         const long cputime_limit =
44                 pid ? PARENT_CPUTIME_LIMIT_NSEC : CHILD_CPUTIME_LIMIT_NSEC;
45
46         /* Enjoying my user time */
47         while (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
48                 if (ts.tv_sec || ts.tv_nsec >= cputime_limit)
49                         break;
50
51                 if (i && !(ts.tv_sec || ts.tv_nsec))
52                         error_msg_and_skip("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, {0, 0})");
53
54                 for (i = 0; i < NUM_USER_ITERS; ++i)
55                         ++dummy;
56         }
57
58         /* Enjoying my system time */
59         while (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
60                 if (ts.tv_sec || ts.tv_nsec >= cputime_limit * 2)
61                         break;
62
63                 sched_yield();
64         }
65
66         if (pid == 0) {
67                 return 0;
68         } else {
69                 wait(NULL);
70         }
71
72         struct tms tbuf;
73         unsigned long long llres;
74
75         /*
76          * On systems where user's and kernel's long types are the same,
77          * prefer direct times syscall over libc's times function because
78          * the latter is more prone to return value truncation.
79          */
80 #undef USE_LIBC_SYSCALL
81 #if defined __NR_times && \
82    !defined(LINUX_MIPSN32) && \
83    !(defined __x86_64__ && defined __ILP32__)
84 # define USE_LIBC_SYSCALL 1
85 #endif
86
87 #if defined USE_LIBC_SYSCALL
88         long res = syscall(__NR_times, &tbuf);
89
90         if (-1L == res)
91                 perror_msg_and_skip("times");
92         else
93                 llres = (unsigned long) res;
94 #elif defined __NR_times && defined __x86_64__ && defined __ILP32__
95         register long arg asm("rdi") = (long) &tbuf;
96         asm volatile("syscall\n\t"
97                      : "=a"(llres)
98                      : "0"(__NR_times), "r"(arg)
99                      : "memory", "cc", "r11", "cx");
100         if (llres > 0xfffffffffffff000)
101                 return 77;
102 #else
103         clock_t res = times(&tbuf);
104
105         if ((clock_t) -1 == res)
106                 perror_msg_and_skip("times");
107         if (sizeof(res) < sizeof(unsigned long long))
108                 llres = (unsigned long) res;
109         else
110                 llres = res;
111 #endif
112
113         printf("times({tms_utime=%llu, tms_stime=%llu, ",
114                 (unsigned long long) tbuf.tms_utime,
115                 (unsigned long long) tbuf.tms_stime);
116         printf("tms_cutime=%llu, tms_cstime=%llu}) = %llu\n",
117                 (unsigned long long) tbuf.tms_cutime,
118                 (unsigned long long) tbuf.tms_cstime,
119                 llres);
120         puts("+++ exited with 0 +++");
121
122         return 0;
123 }