blob: 68c0cf487f068717cfb734ee2acdbbd51d29cebd [file] [log] [blame]
Kamil Koczurek567fc0f2020-06-19 10:56:41 +02001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <fcntl.h>
7#include <inttypes.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <time.h>
13#include <unistd.h>
14#include <sys/time.h>
15
16#define SIG SIGRTMIN
17
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020018typedef struct {
19 const char* name;
20 clockid_t id;
21} named_clock_t;
22
Kamil Koczurek567fc0f2020-06-19 10:56:41 +020023static int out_desc = STDOUT_FILENO; /* default is stdout, prog param */
24
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020025// Make sure to keep this array in sync with `clock*` constants in the test
26// hardware.VerifyRemoteSleep.
27static named_clock_t clocks[] = {
28 {"CLOCK_REALTIME", CLOCK_REALTIME},
29 {"CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE},
30 {"CLOCK_MONOTONIC", CLOCK_MONOTONIC},
31 {"CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE},
32 {"CLOCK_MONOTONIC_RAW", CLOCK_MONOTONIC_RAW},
33 {"CLOCK_BOOTTIME", CLOCK_BOOTTIME},
34 {"CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID},
35 {"CLOCK_THREAD_CPUTIME_ID", CLOCK_THREAD_CPUTIME_ID}
36};
37static size_t clock_n = sizeof(clocks) / sizeof(named_clock_t);
38static clockid_t clockid;
39
Kamil Koczurek567fc0f2020-06-19 10:56:41 +020040static void send_msg(const char *msg) {
41 if(write(out_desc, msg, strlen(msg)) < 0) {
42 const char *err = "[ERR] send error\n";
43 /* printf isn't signal-safe
44 * as this is just error print we do not retry in case of err */
45 (void)write(STDOUT_FILENO, err, strlen(err));
46 exit(EXIT_FAILURE);
47 }
48}
49
50static void handler(int sig) {
51 struct timespec ts;
52
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020053 if (clock_gettime(clockid, &ts)) {
Kamil Koczurek567fc0f2020-06-19 10:56:41 +020054 send_msg("clock_gettimeERR: ping\n");
55 } else {
56 char msg[256];
57
58 snprintf(msg, sizeof(msg) - 1, "%" PRIu64 ".%09lu ping\n",
59 (uint64_t)ts.tv_sec, ts.tv_nsec);
60 send_msg(msg);
61 }
62}
63
64#define LLDIGITS10_MAX ((size_t)19)
65static long long safe_atoll(const char *str) {
66 char *endptr = NULL;
67 long long res = strtoll(str, &endptr, 10);
68
69 if(endptr - str != strnlen(str, LLDIGITS10_MAX)) {
70 fprintf(stderr, "[ERR] `%s` isn't an integer\n", str);
71 exit(EXIT_FAILURE);
72 }
73
74 return res;
75}
76
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020077static void print_help(char* argv[]) {
78 fprintf(stderr, "Usage: %s <time ms> <repetitions> <clock> [out file]\n",
79 argv[0]);
80 fprintf(stderr, "supported clocks: \n");
81 for(size_t i = 0; i < clock_n; ++i) {
82 fprintf(stderr, "- %s\n", clocks[i].name);
83 }
84}
85
Kamil Koczurek567fc0f2020-06-19 10:56:41 +020086int main(int argc, char* argv[]) {
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020087 if (argc != 5 && argc != 4) {
88 print_help(argv);
Kamil Koczurek567fc0f2020-06-19 10:56:41 +020089 exit(EXIT_FAILURE);
90 }
91
92 long long msecs = safe_atoll(argv[1]);
93 long long iterations = safe_atoll(argv[2]);
94
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +020095 // sets the global clockid
96 for(size_t i = 0; i <= clock_n; ++i) {
97 /* found no matching clock */
98 if(i == clock_n) {
99 print_help(argv);
100 fprintf(stderr, "supplied clock: '%s'\n", argv[3]);
101 exit(EXIT_FAILURE);
102 }
103
104 if(strcmp(argv[3], clocks[i].name) == 0) {
105 clockid = clocks[i].id;
106 break;
107 }
108 }
109
110 // change the output descriptor if a 4th argument is supplied
111 if (argc == 5) {
112 out_desc = open(argv[4], O_WRONLY);
Kamil Koczurek567fc0f2020-06-19 10:56:41 +0200113 if(out_desc < 0) {
114 fprintf(stderr, "Couldn't open file `%s`, errno: %d\n",
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +0200115 argv[4], errno);
Kamil Koczurek567fc0f2020-06-19 10:56:41 +0200116 exit(EXIT_FAILURE);
117 }
118 }
119
120 // Establish handler for timer signal
121 struct sigaction sa;
122 memset(&sa, 0, sizeof(sa));
123 sa.sa_handler = handler;
124 sigemptyset(&sa.sa_mask); /* just this sig will be in mask, no SA_NODEFER */
125 if (sigaction(SIG, &sa, NULL)) {
126 fprintf(stderr, "[ERR] Couldn't set signal disposition, errno: %d\n",
127 errno);
128 exit(EXIT_FAILURE);
129 }
130
131 // Create the timer
132 struct sigevent sev;
133 timer_t timerid;
134 memset(&sev, 0, sizeof(sev));
135 sev.sigev_notify = SIGEV_SIGNAL;
136 sev.sigev_signo = SIG;
137 sev.sigev_value.sival_ptr = &timerid;
Kamil Koczurek16c0f6d2020-08-28 11:13:05 +0200138 if (timer_create(clockid, &sev, &timerid) == -1) {
Kamil Koczurek567fc0f2020-06-19 10:56:41 +0200139 fprintf(stderr, "[ERR] Couldn't create the timer: %d\n", errno);
140 exit(EXIT_FAILURE);
141 }
142
143 // Start the timer
144 struct itimerspec its;
145 memset(&its, 0, sizeof(its));
146 its.it_value.tv_sec = 0;
147 its.it_value.tv_nsec = 10000000; // Timer will not start when 0
148 its.it_interval.tv_sec = msecs / 1000;
149 its.it_interval.tv_nsec = (msecs % 1000) * 1000000;
150
151 if (timer_settime(timerid, 0, &its, NULL) == -1) {
152 fprintf(stderr, "[ERR] Couldn't set timer time: %d\n", errno);
153 exit(EXIT_FAILURE);
154 }
155
156 for(int i = 0; i < iterations + 1; ++i) {
157 if(pause() < 0 && errno == EINTR && out_desc)
158 /* in case out_desc is not stdout */
159 printf("[INFO] Sent msg\n");
160 }
161
162 exit(EXIT_SUCCESS);
163}