1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <getopt.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23
24 #include <sys/epoll.h>
25 #include <sys/timerfd.h>
26
27 #include <cutils/klog.h>
28
29 #define NSEC_PER_SEC (1000*1000*1000)
30 #define MSEC_PER_SEC 1000
31 #define NSEC_PER_MSEC (NSEC_PER_SEC/MSEC_PER_SEC)
32
timediff_ns(const struct timespec * a,const struct timespec * b)33 long long timediff_ns(const struct timespec *a, const struct timespec *b) {
34 return ((long long)(a->tv_sec - b->tv_sec)) * NSEC_PER_SEC +
35 (a->tv_nsec - b->tv_nsec);
36 }
37
usage(void)38 void usage(void)
39 {
40 printf("usage: suspend_stress [ <options> ]\n"
41 "options:\n"
42 " -a,--abort abort test on late alarm\n"
43 " -c,--count=<count> number of times to suspend (default infinite)\n"
44 " -t,--time=<seconds> time to suspend for (default 5)\n"
45 );
46 }
47
main(int argc,char ** argv)48 int main(int argc, char **argv)
49 {
50 int alarm_time = 5;
51 int count = -1;
52 bool abort_on_failure = false;
53
54 while (1) {
55 const static struct option long_options[] = {
56 {"abort", no_argument, 0, 'a'},
57 {"count", required_argument, 0, 'c'},
58 {"time", required_argument, 0, 't'},
59 };
60 int c = getopt_long(argc, argv, "ac:t:", long_options, NULL);
61 if (c < 0) {
62 break;
63 }
64
65 switch (c) {
66 case 'a':
67 abort_on_failure = true;
68 break;
69 case 'c':
70 count = strtoul(optarg, NULL, 0);
71 break;
72 case 't':
73 alarm_time = strtoul(optarg, NULL, 0);
74 break;
75 case '?':
76 usage();
77 exit(EXIT_FAILURE);
78 default:
79 abort();
80 }
81 }
82
83 klog_init();
84 klog_set_level(KLOG_INFO_LEVEL);
85
86 if (optind < argc) {
87 fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
88 usage();
89 exit(EXIT_FAILURE);
90 }
91
92 int fd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
93 if (fd < 0) {
94 perror("timerfd_create failed");
95 exit(EXIT_FAILURE);
96 }
97
98 struct itimerspec delay = itimerspec();
99 delay.it_value.tv_sec = alarm_time;
100 int i = 0;
101
102 int epoll_fd = epoll_create(1);
103 if (epoll_fd < 0) {
104 perror("epoll_create failed");
105 exit(EXIT_FAILURE);
106 }
107
108 struct epoll_event ev = epoll_event();
109 ev.events = EPOLLIN | EPOLLWAKEUP;
110 int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
111 if (ret < 0) {
112 perror("epoll_ctl failed");
113 exit(EXIT_FAILURE);
114 }
115
116 while (count != 0) {
117 struct timespec expected_time;
118 struct timespec actual_time;
119 uint64_t fired = 0;
120
121 ret = timerfd_settime(fd, 0, &delay, NULL);
122 if (ret < 0) {
123 perror("timerfd_settime failed");
124 exit(EXIT_FAILURE);
125 }
126
127 ret = clock_gettime(CLOCK_BOOTTIME, &expected_time);
128 if (ret < 0) {
129 perror("failed to get time");
130 exit(EXIT_FAILURE);
131 }
132 expected_time.tv_sec += alarm_time;
133
134 ret = 0;
135 while (ret != 1) {
136 struct epoll_event out_ev;
137 ret = epoll_wait(epoll_fd, &out_ev, 1, -1);
138 if (ret < 0 && errno != EINTR) {
139 perror("epoll_wait failed");
140 exit(EXIT_FAILURE);
141 }
142 }
143
144 ssize_t bytes = read(fd, &fired, sizeof(fired));
145 if (bytes < 0) {
146 perror("read from timer fd failed");
147 exit(EXIT_FAILURE);
148 } else if (bytes < (ssize_t)sizeof(fired)) {
149 fprintf(stderr, "unexpected read from timer fd: %zd\n", bytes);
150 }
151
152 if (fired != 1) {
153 fprintf(stderr, "unexpected timer fd fired count: %" PRIu64 "\n", fired);
154 }
155
156 ret = clock_gettime(CLOCK_BOOTTIME, &actual_time);
157 if (ret < 0) {
158 perror("failed to get time");
159 exit(EXIT_FAILURE);
160 }
161
162 long long diff = timediff_ns(&actual_time, &expected_time);
163 if (llabs(diff) > NSEC_PER_SEC) {
164 fprintf(stderr, "alarm arrived %lld.%03lld seconds %s\n",
165 llabs(diff) / NSEC_PER_SEC,
166 (llabs(diff) / NSEC_PER_MSEC) % MSEC_PER_SEC,
167 diff > 0 ? "late" : "early");
168 KLOG_ERROR("suspend_stress", "alarm arrived %lld.%03lld seconds %s\n",
169 llabs(diff) / NSEC_PER_SEC,
170 (llabs(diff) / NSEC_PER_MSEC) % MSEC_PER_SEC,
171 diff > 0 ? "late" : "early");
172 if (abort_on_failure) {
173 exit(EXIT_FAILURE);
174 }
175 }
176
177 time_t t = time(NULL);
178 i += fired;
179 printf("timer fired: %d at boottime %lld.%.3ld, %s", i,
180 (long long)actual_time.tv_sec,
181 actual_time.tv_nsec / NSEC_PER_MSEC,
182 ctime(&t));
183
184 KLOG_INFO("suspend_stress", "timer fired: %d at boottime %lld.%.3ld, %s", i,
185 (long long)actual_time.tv_sec,
186 actual_time.tv_nsec / NSEC_PER_MSEC,
187 ctime(&t));
188
189 if (count > 0)
190 count--;
191 }
192 return 0;
193 }
194