1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under MIT License.
5 // Full license text is available in 'licenses/MIT.txt' file.
6 //
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include "renode_api.h"
14
15 #define LINE_BUFFER_SIZE 86
16
get_error_message(renode_error_t * error)17 char *get_error_message(renode_error_t *error)
18 {
19 if (error->message == NULL)
20 {
21 return "<no message>";
22 }
23 return error->message;
24 }
25
26 #ifdef LOG_PERF
27 #include <time.h>
28
29 static int tsi;
30 static struct timespec t[2];
31
perf_start()32 static void perf_start()
33 {
34 clock_gettime(CLOCK_REALTIME, &t[tsi]);
35 }
36
perf_stop(uint64_t times,uint64_t value,renode_time_unit_t time_unit)37 static void perf_stop(uint64_t times, uint64_t value, renode_time_unit_t time_unit)
38 {
39 tsi ^= 1;
40 clock_gettime(CLOCK_REALTIME, &t[tsi]);
41
42 int64_t seconds = t[tsi].tv_sec - t[tsi ^ 1].tv_sec;
43 int64_t nanoseconds = t[tsi].tv_nsec - t[tsi ^ 1].tv_nsec;
44 if(nanoseconds < 0)
45 {
46 seconds -= 1;
47 nanoseconds += 1000000000;
48 }
49 int64_t microseconds = (nanoseconds + 500) / 1000;
50
51 int64_t virt_seconds = value * times / (time_unit == TU_SECONDS ? 1 : (time_unit == TU_MICROSECONDS ? 1000000 : 1000));
52 int64_t virt_microseconds = (value * times) % (time_unit == TU_SECONDS ? 1 : (time_unit == TU_MICROSECONDS ? 1000000 : 1000));
53
54 fprintf(stderr, "delta: %lu.%06lus real\t %lu.%06lus virt\t real / virt %lf\n", seconds, microseconds, virt_seconds, virt_microseconds, (seconds * 1e6 + microseconds) / (virt_seconds * 1e6 + virt_microseconds));
55 }
56
start_virtual_time_check(renode_t * renode)57 static void start_virtual_time_check(renode_t *renode)
58 {
59 (void)renode;
60 }
61
stop_virtual_time_check(renode_t * renode,uint64_t value,renode_time_unit_t time_unit)62 static void stop_virtual_time_check(renode_t *renode, uint64_t value, renode_time_unit_t time_unit)
63 {
64 (void)renode;
65 (void)value;
66 (void)time_unit;
67 }
68
69 #else // !LOG_PERF
70
perf_start()71 static void perf_start() {}
72
perf_stop(uint64_t times,uint64_t value,renode_time_unit_t time_unit)73 static void perf_stop(uint64_t times, uint64_t value, renode_time_unit_t time_unit)
74 {
75 (void)times;
76 (void)value;
77 (void)time_unit;
78 }
79
80 static uint64_t vt0;
81
get_current_virtual_time(renode_t * renode)82 static uint64_t get_current_virtual_time(renode_t *renode)
83 {
84 renode_error_t *error;
85 uint64_t current_time;
86 if ((error = renode_get_current_time(renode, TU_MICROSECONDS, ¤t_time)) != NO_ERROR) {
87 fprintf(stderr, "Get current time failed with: %s\n", get_error_message(error));
88 renode_free_error(error);
89 exit(EXIT_FAILURE);
90 }
91
92 return current_time;
93 }
94
start_virtual_time_check(renode_t * renode)95 static void start_virtual_time_check(renode_t *renode)
96 {
97 vt0 = get_current_virtual_time(renode);
98 }
99
stop_virtual_time_check(renode_t * renode,uint64_t value,renode_time_unit_t time_unit)100 static void stop_virtual_time_check(renode_t *renode, uint64_t value, renode_time_unit_t time_unit)
101 {
102 uint64_t vt1 = get_current_virtual_time(renode);
103 uint64_t vt_delta = vt1 - vt0;
104
105 int microseconds = vt1 % TU_SECONDS;
106 int seconds_total = vt1 / TU_SECONDS;
107 int seconds = seconds_total % 60;
108 int minutes_total = seconds_total / 60;
109 int minutes = minutes_total % 60;
110 int hours_total = minutes_total / 60;
111
112 printf("Elapsed virtual time %02d:%02d:%02d.%06d\n", hours_total, minutes, seconds, microseconds);
113
114 if (vt_delta != value * time_unit) {
115 fprintf(stderr, "Reported current virtual time doesn't match the expected virtual time after running for the requested interval\n");
116 exit(EXIT_FAILURE);
117 }
118 }
119
120 #endif
121
exit_with_usage_info(const char * argv0)122 void exit_with_usage_info(const char *argv0)
123 {
124 fprintf(stderr,
125 "Usage:\n"
126 " %s <PORT> <VALUE_WITH_UNIT> [<REPEAT>]\n"
127 " where:\n"
128 " * <VALUE_WITH_UNIT> is an integer with a time unit, e.g.: '100ms'\n"
129 " * accepted time units are 's', 'ms' and 'us' (for microseconds)\n"
130 " * <REPEAT> is an optional number of times (default: 1) to run\n"
131 " * the simulation for\n",
132 argv0);
133 exit(EXIT_FAILURE);
134 }
135
read_time_value(char * buffer,char ** endptr,uint64_t * value,renode_time_unit_t * time_unit)136 bool read_time_value(char *buffer, char **endptr, uint64_t *value, renode_time_unit_t *time_unit)
137 {
138 char *noendptr;
139 if (endptr == NULL) {
140 endptr = &noendptr;
141 }
142 // base=0 tries to figure out the number's base automatically.
143 uint64_t new_value = strtoull(buffer, endptr, /* base: */ 0);
144 if (errno != 0) {
145 perror("conversion to uint64_t value");
146 exit(EXIT_FAILURE);
147 }
148
149 if (*endptr == buffer) {
150 return false;
151 }
152
153 renode_time_unit_t unit = -1;
154 switch (**endptr) {
155 case 'u':
156 unit = TU_MICROSECONDS;
157 *endptr += 2;
158 break;
159 case 'm':
160 unit = TU_MILLISECONDS;
161 *endptr += 2;
162 break;
163 case 's':
164 unit = TU_SECONDS;
165 *endptr += 1;
166 break;
167 default:
168 return false;
169 }
170
171 *value = new_value;
172 *time_unit = unit;
173 return true;
174 }
175
run_options_prompt(uint64_t * value,renode_time_unit_t * time_unit,uint64_t * times)176 void run_options_prompt(uint64_t *value, renode_time_unit_t *time_unit, uint64_t *times)
177 {
178 char option;
179 bool retry;
180
181 do {
182 retry = false;
183 printf("Continue running for %lu%ss %lu times? [y/N/c] ", *value, *time_unit == TU_MICROSECONDS ? "u" : (*time_unit == TU_MILLISECONDS ? "m" : ""), *times);
184
185 if ((option = getchar()) != '\n') while(getchar() != '\n');
186
187 switch (option)
188 {
189 case 'y':
190 case 'Y':
191 return;
192 case 'c':
193 case 'C':
194 printf("Enter new value: ");
195 {
196 char buffer[LINE_BUFFER_SIZE];
197 char *endptr;
198
199 if (fgets(buffer, LINE_BUFFER_SIZE, stdin) != buffer) {
200 fprintf(stderr, "Failed to read line\n");
201 retry = true;
202 break;
203 }
204
205 size_t len = strlen(buffer);
206 if (len == LINE_BUFFER_SIZE - 1 && buffer[len - 1] != '\n') {
207 fprintf(stderr, "Failed to read line: line too long\n");
208 retry = true;
209 break;
210 }
211
212 errno = 0;
213 if (!read_time_value(buffer, &endptr, value, time_unit)) {
214 fprintf(stderr, "Failed to parse time value\n");
215 retry = true;
216 break;
217 }
218
219 if (*endptr != '\n' && *endptr != '\0')
220 {
221 *times = strtoull(endptr, NULL, /* base: */ 0);
222 if (errno != 0) {
223 perror("conversion to uint64_t value");
224 exit(EXIT_FAILURE);
225 }
226 }
227 else
228 {
229 *times = 1;
230 }
231 }
232 return;
233 case '\n':
234 case 'n':
235 case 'N':
236 *times = 0;
237 return;
238 default:
239 retry = true;
240 break;
241 }
242
243 } while(retry);
244 }
245
main(int argc,char ** argv)246 int main(int argc, char **argv)
247 {
248 if (argc < 3 || 4 < argc) {
249 exit_with_usage_info(argv[0]);
250 }
251
252 uint64_t value;
253 renode_time_unit_t time_unit;
254 if (!read_time_value(argv[2], NULL, &value, &time_unit)) {
255 exit_with_usage_info(argv[0]);
256 }
257
258 uint64_t times = 1;
259 if (argc == 4)
260 {
261 times = strtoull(argv[3], NULL, /* base: */ 0);
262 if (errno != 0) {
263 perror("conversion to uint64_t value");
264 exit(EXIT_FAILURE);
265 }
266 }
267
268 renode_error_t *error;
269 renode_t *renode;
270 if ((error = renode_connect(argv[1], &renode)) != NO_ERROR) {
271 fprintf(stderr, "Connecting to Renode failed with: %s\n", get_error_message(error));
272 renode_free_error(error);
273 exit(EXIT_FAILURE);
274 }
275
276 perf_start();
277
278 uint64_t i = times;
279 while(i > 0)
280 {
281 start_virtual_time_check(renode);
282
283 if ((error = renode_run_for(renode, time_unit, value)) != NO_ERROR) {
284 fprintf(stderr, "Run for failed with: %s\n", get_error_message(error));
285 renode_free_error(error);
286 exit(EXIT_FAILURE);
287 }
288
289 stop_virtual_time_check(renode, value, time_unit);
290
291 i -= 1;
292 if (i == 0)
293 {
294 perf_stop(times, value, time_unit);
295 run_options_prompt(&value, &time_unit, ×);
296 i = times;
297 perf_start();
298 }
299 }
300
301 if ((error = renode_disconnect(&renode)) != NO_ERROR) {
302 fprintf(stderr, "Disconnecting from Renode failed with: %s\n", get_error_message(error));
303 renode_free_error(error);
304 exit(EXIT_FAILURE);
305 }
306
307 exit(EXIT_SUCCESS);
308 }
309