forked from linaro-swg/optee_benchmark
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
476 lines (393 loc) · 11.6 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/*
* Copyright (c) 2016, Linaro Limited
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <libgen.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <yaml.h>
#include "benchmark_aux.h"
#include "common.h"
#define MAX_SCALAR 20
static struct tee_ts_global *bench_ts_global;
static const TEEC_UUID pta_benchmark_uuid = PTA_BENCHMARK_UUID;
static TEEC_Context ctx;
static TEEC_Session sess;
static sig_atomic_t is_running;
static yaml_emitter_t emitter;
struct consumer_param {
pid_t child_pid;
char *ts_filepath;
};
void sigint_handler(int data)
{
(void)data;
is_running = 0;
}
static void open_bench_pta(void)
{
TEEC_Result res;
uint32_t err_origin;
res = TEEC_InitializeContext(NULL, &ctx);
tee_check_res(res, "TEEC_InitializeContext");
res = TEEC_OpenSession(&ctx, &sess, &pta_benchmark_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
tee_check_res(res, "TEEC_OpenSession");
}
static void close_bench_pta(void)
{
/* release benchmark timestamp shm */
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
}
static void alloc_bench_buf(uint32_t cores)
{
TEEC_Result res;
TEEC_Operation op = { 0 };
uint32_t ret_orig;
intptr_t paddr_ts_buf = 0;
size_t size;
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT,
TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE);
op.params[1].value.a = cores;
res = TEEC_InvokeCommand(&sess, BENCHMARK_CMD_REGISTER_MEMREF,
&op, &ret_orig);
tee_check_res(res, "TEEC_InvokeCommand");
paddr_ts_buf = op.params[0].value.a;
size = op.params[0].value.b;
INFO("ts buffer paddr = %" PRIiPTR ", size = %zu\n", paddr_ts_buf, size);
if (paddr_ts_buf) {
bench_ts_global = mmap_paddr(paddr_ts_buf, size);
if (!bench_ts_global)
ERROR_EXIT("Failed to allocate timestamp buffer");
} else {
ERROR_EXIT("Failed to allocate timestamp buffer");
}
}
static void free_bench_buf(void)
{
TEEC_Result res;
TEEC_Operation op = { 0 };
uint32_t ret_orig;
DBG("Freeing benchmark buffer.");
op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE,
TEEC_NONE, TEEC_NONE, TEEC_NONE);
res = TEEC_InvokeCommand(&sess, BENCHMARK_CMD_UNREGISTER,
&op, &ret_orig);
tee_check_res(res, "TEEC_InvokeCommand");
}
static void usage(char *progname)
{
fprintf(stderr, "Call latency benchmark tool for OP-TEE\n\n");
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s -h\n", progname);
fprintf(stderr, " %s host_app [host_app_args]\n", progname);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -h Print this help and exit\n");
fprintf(stderr, " host_app Path to host app to benchmark\n");
fprintf(stderr, " host_app_args Original host app args\n");
}
static int timestamp_pop(struct tee_ts_cpu_buf *cpu_buf,
struct tee_time_st *ts)
{
uint64_t ts_tail;
if (!cpu_buf && !ts)
return RING_BADPARM;
if (cpu_buf->tail >= cpu_buf->head)
return RING_NODATA;
ts_tail = cpu_buf->tail++;
*ts = cpu_buf->stamps[ts_tail & TEE_BENCH_MAX_MASK];
return 0;
}
static bool init_emitter(FILE *ts_file)
{
yaml_event_t event;
if (!yaml_emitter_initialize(&emitter))
ERROR_RETURN_FALSE("Can't initialize YAML emitter");
yaml_emitter_set_canonical(&emitter, 0);
yaml_emitter_set_unicode(&emitter, 1);
yaml_emitter_set_output_file(&emitter, ts_file);
/* Stream start */
if (!yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML stream start event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML stream start event");
/* Document start */
if (!yaml_document_start_event_initialize(&event,
NULL, NULL, NULL, YAML_IMPLICIT))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML document start event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML doc start event");
/* Mapping start */
if (!yaml_mapping_start_event_initialize(&event,
NULL, NULL , YAML_IMPLICIT,
YAML_ANY_MAPPING_STYLE))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML mapping start event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML sequence mapping event");
/* Key timestamps */
yaml_scalar_event_initialize(&event, NULL, NULL,
(yaml_char_t *)"timestamps", -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
if (!yaml_emitter_emit(&emitter, &event))
ERROR_RETURN_FALSE("Failed to emit YAML scalar");
/* Sequence start */
if (!yaml_sequence_start_event_initialize(&event,
NULL, NULL , YAML_IMPLICIT,
YAML_ANY_SEQUENCE_STYLE))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML sequence start event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML sequence start event");
return true;
emitter_delete:
yaml_emitter_delete(&emitter);
return false;
}
static void deinit_emitter()
{
yaml_event_t event;
/* Sequence cmd */
if (!yaml_sequence_end_event_initialize(&event))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML sequence end event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML sequence end event");
/* Mapping end */
if (!yaml_mapping_end_event_initialize(&event))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML mapping end event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML mapping end event");
/* Document end */
if (!yaml_document_end_event_initialize(&event, 0))
ERROR_GOTO(emitter_delete,
"Failed to initialize YAML document end event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete, "Failed to emit YAML doc end event");
/* Stream end */
if (!yaml_stream_end_event_initialize(&event))
ERROR_GOTO(emitter_delete,
"Failed to initialise YAML stream end event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_GOTO(emitter_delete,
"Failed to emit YAML stream end event");
emitter_delete:
yaml_emitter_delete(&emitter);
}
static bool fill_map(char *var, char *value)
{
yaml_event_t event;
yaml_scalar_event_initialize(&event, NULL, NULL,
(yaml_char_t *)var, -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
if (!yaml_emitter_emit(&emitter, &event))
ERROR_RETURN_FALSE("Failed to emit YAML scalar");
yaml_scalar_event_initialize(&event, NULL, NULL,
(yaml_char_t *)value, -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
if (!yaml_emitter_emit(&emitter, &event))
ERROR_RETURN_FALSE("Failed to emit YAML scalar");
return true;
}
static bool fill_timestamp(uint32_t core, uint64_t count, uint64_t addr,
const char *subsystem)
{
yaml_event_t event;
char data[MAX_SCALAR];
/* Mapping start */
if (!yaml_mapping_start_event_initialize(&event,
NULL, NULL , YAML_IMPLICIT,
YAML_ANY_MAPPING_STYLE))
ERROR_RETURN_FALSE(
"Failed to initialize YAML mapping start event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_RETURN_FALSE("Failed to emit YAML mapping start event");
snprintf(data, MAX_SCALAR, "%" PRIu32, core);
fill_map("core", data);
snprintf(data, MAX_SCALAR, "%" PRIu64, count);
fill_map("counter", data);
snprintf(data, MAX_SCALAR, "0x%" PRIx64, addr);
fill_map("address", data);
snprintf(data, MAX_SCALAR, "%s", subsystem);
fill_map("component", data);
/* Mapping end */
if (!yaml_mapping_end_event_initialize(&event))
ERROR_RETURN_FALSE(
"Failed to initialize YAML mapping end event");
if (!yaml_emitter_emit(&emitter, &event))
ERROR_RETURN_FALSE("Failed to emit YAML mapping end event");
return true;
}
/*
* Consume all timestamps from per-cpu ringbuffers and put everything into
* the yaml file.
*/
static void *ts_consumer(void *arg)
{
unsigned int i;
int ret;
bool ts_received = false;
uint32_t cores;
struct tee_time_st ts_data;
FILE *ts_file;
struct consumer_param *prm = (struct consumer_param *)arg;
char *tsfile_path = prm->ts_filepath;
pid_t child_pid = prm->child_pid;
size_t teec_dyn_addr = 0;
if (!tsfile_path)
ERROR_GOTO(exit, "Wrong timestamp file path");
cores = get_cores();
if (!cores)
ERROR_GOTO(exit, "Can't receive amount of avalable cores");
ts_file = fopen(tsfile_path, "w");
if (!ts_file)
ERROR_GOTO(exit, "Can't open timestamp file");
if (!init_emitter(ts_file))
ERROR_GOTO(file_close,
"Error occurred in emitter initialization");
while (is_running) {
ts_received = false;
for (i = 0; i < cores; i++) {
ret = timestamp_pop(&bench_ts_global->cpu_buf[i],
&ts_data);
if (!ret) {
ts_received = true;
DBG("Timestamp: core = %u; tick = %lld; "
"pc = 0x%" PRIx64 "; system = %s",
i, ts_data.cnt, ts_data.addr,
bench_str_src(ts_data.src));
do {
teec_dyn_addr = get_library_load_offset
(child_pid,
LIBTEEC_NAME);
} while (!teec_dyn_addr && is_running);
if (ts_data.src == TEE_BENCH_CLIENT) {
DBG("ts_addr = %llx, teec_addr = %x",
ts_data.addr, teec_dyn_addr);
ts_data.addr -= teec_dyn_addr;
}
if (!fill_timestamp(i, ts_data.cnt,
ts_data.addr,
bench_str_src(ts_data.src)))
ERROR_GOTO(deinit_yaml,
"Adding timestamp failed");
}
}
if (!ts_received) {
if (is_running) {
DBG("yielding...");
sched_yield();
} else {
ERROR_GOTO(deinit_yaml,
"No new data in the per-cpu ringbuffers"
);
}
}
}
deinit_yaml:
deinit_emitter();
file_close:
fclose(ts_file);
exit:
return NULL;
}
int main(int argc, char *argv[])
{
int i;
int status;
pid_t pid;
char testapp_path[PATH_MAX];
char **testapp_argv;
char *res;
char *tsfile_path;
uint32_t cores;
pthread_t consumer_thread;
struct consumer_param prm;
if (argc == 1) {
usage(argv[0]);
return 0;
}
/* Parse command line */
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h")) {
usage(argv[0]);
return 0;
}
}
signal(SIGINT, sigint_handler);
INFO("1. Opening Benchmark Static TA...");
open_bench_pta();
cores = get_cores();
if (!cores)
ERROR_EXIT("Receiving amount of active cores failed");
INFO("2. Allocating per-core buffers, cores detected = %d",
cores);
alloc_bench_buf(cores);
res = realpath(argv[1], testapp_path);
if (!res)
tee_errx("Failed to get realpath", EXIT_FAILURE);
alloc_argv(argc, argv, &testapp_argv);
INFO("3. Starting origin host app %s ...", testapp_path);
/* fork/exec here */
pid = fork();
if (pid == -1) {
DBG("fork() failed");
ERROR_EXIT("Starting origin host application failed.");
} else if (pid > 0) {
is_running = 1;
tsfile_path = malloc(strlen(testapp_path) +
strlen(TSFILE_NAME_SUFFIX) + 1);
if (!tsfile_path)
ERROR_EXIT("Memory allocation failed the file path.");
tsfile_path[0] = '\0';
strcat(tsfile_path, testapp_path);
strcat(tsfile_path, TSFILE_NAME_SUFFIX);
INFO("Dumping timestamps to %s ...", tsfile_path);
print_line();
prm.child_pid = pid;
prm.ts_filepath = tsfile_path;
if (pthread_create(&consumer_thread, NULL,
ts_consumer, &prm)) {
DBG( "Error creating ts consumer thread");
ERROR_EXIT("Can't start process of reading timestamps");
}
/* wait for child app exits */
waitpid(pid, &status, 0);
DBG("Origin host application finished executing");
is_running = 0;
/* wait for our consumer thread terminate */
if (pthread_join(consumer_thread, NULL)) {
DBG("Error joining thread");
ERROR_EXIT("Couldn't start consuming timestamps");
}
}
else {
execvp(testapp_path, testapp_argv);
DBG("execve() failed");
ERROR_EXIT("Starting origin host application failed");
}
INFO("4. Done benchmark");
dealloc_argv(argc-1, testapp_argv);
free_bench_buf();
close_bench_pta();
return 0;
}