1 /* 2 * Copyright (c) 2015 Eugene Syromyatnikov <evgsyr (at) gmail.com> 3 * Copyright (c) 2015 Dmitry V. Levin <ldv (at) altlinux.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This test burns some CPU cycles in user space and kernel space in order to 32 * get some non-zero values returned by times(2). 33 */ 34 35 #ifdef HAVE_CONFIG_H 36 # include "config.h" 37 #endif 38 39 #include <sched.h> 40 #include <stdio.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include <sys/syscall.h> 45 #include <sys/times.h> 46 #include <sys/wait.h> 47 48 enum { 49 NUM_USER_ITERS = 1000000, 50 PARENT_CPUTIME_LIMIT_NSEC = 200000000, 51 CHILD_CPUTIME_LIMIT_NSEC = 300000000 52 }; 53 54 int 55 main (void) 56 { 57 struct timespec ts; 58 volatile int dummy; 59 int i; 60 61 pid_t pid = fork(); 62 63 if (pid < 0) 64 return 77; 65 66 const long cputime_limit = 67 pid ? PARENT_CPUTIME_LIMIT_NSEC : CHILD_CPUTIME_LIMIT_NSEC; 68 69 /* Enjoying my user time */ 70 while (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) { 71 if (ts.tv_sec || ts.tv_nsec >= cputime_limit) 72 break; 73 74 for (i = 0; i < NUM_USER_ITERS; ++i) 75 ++dummy; 76 } 77 78 /* Enjoying my system time */ 79 while (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) { 80 if (ts.tv_sec || ts.tv_nsec >= cputime_limit * 2) 81 break; 82 83 sched_yield(); 84 } 85 86 if (pid == 0) { 87 return 0; 88 } else { 89 wait(NULL); 90 } 91 92 struct tms tbuf; 93 unsigned long long llres; 94 95 /* 96 * On systems where user's and kernel's long types are the same, 97 * prefer direct times syscall over libc's times function because 98 * the latter is more prone to return value truncation. 99 */ 100 #undef USE_LIBC_SYSCALL 101 #if defined __NR_times && \ 102 !defined(LINUX_MIPSN32) && \ 103 !(defined __x86_64__ && defined __ILP32__) 104 # define USE_LIBC_SYSCALL 1 105 #endif 106 107 #if defined USE_LIBC_SYSCALL 108 long res = syscall(__NR_times, &tbuf); 109 110 if (-1L == res) 111 return 77; 112 else 113 llres = (unsigned long) res; 114 #elif defined __NR_times && defined __x86_64__ && defined __ILP32__ 115 register long arg asm("rdi") = (long) &tbuf; 116 asm volatile("syscall\n\t" 117 : "=a"(llres) 118 : "0"(__NR_times), "r"(arg) 119 : "memory", "cc", "r11", "cx"); 120 if (llres > 0xfffffffffffff000) 121 return 77; 122 #else 123 clock_t res = times(&tbuf); 124 125 if ((clock_t) -1 == res) 126 return 77; 127 if (sizeof(res) < sizeof(unsigned long long)) 128 llres = (unsigned long) res; 129 else 130 llres = res; 131 #endif 132 133 printf("times({tms_utime=%llu, tms_stime=%llu, ", 134 (unsigned long long) tbuf.tms_utime, 135 (unsigned long long) tbuf.tms_stime); 136 printf("tms_cutime=%llu, tms_cstime=%llu}) = %llu\n", 137 (unsigned long long) tbuf.tms_cutime, 138 (unsigned long long) tbuf.tms_cstime, 139 llres); 140 puts("+++ exited with 0 +++"); 141 142 return 0; 143 } 144