1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2019, Linaro Limited
4 */
5
6 #include <dirent.h>
7 #include <err.h>
8 #include <errno.h>
9 #include <fnmatch.h>
10 #include <inttypes.h>
11 #include <stdbool.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <tee_client_api.h>
19 #include <unistd.h>
20 #include "xtest_helpers.h"
21 #include "xtest_test.h"
22 #include "stats.h"
23
24 #define STATS_UUID { 0xd96a5b40, 0xe2c7, 0xb1af, \
25 { 0x87, 0x94, 0x10, 0x02, 0xa5, 0xd5, 0xc6, 0x1b } }
26
27 #define STATS_CMD_PAGER_STATS 0
28 #define STATS_CMD_ALLOC_STATS 1
29 #define STATS_CMD_MEMLEAK_STATS 2
30 #define STATS_CMD_TA_STATS 3
31
32 #define TEE_ALLOCATOR_DESC_LENGTH 32
33 struct malloc_stats {
34 char desc[TEE_ALLOCATOR_DESC_LENGTH];
35 uint32_t allocated; /* Bytes currently allocated */
36 uint32_t max_allocated; /* Tracks max value of allocated */
37 uint32_t size; /* Total size for this allocator */
38 uint32_t num_alloc_fail; /* Number of failed alloc requests */
39 uint32_t biggest_alloc_fail; /* Size of biggest failed alloc */
40 uint32_t biggest_alloc_fail_used; /* Alloc bytes when above occurred */
41 };
42
43 struct ta_dump_stats {
44 TEEC_UUID uuid;
45 uint32_t panicked; /* True if TA has panicked */
46 uint32_t sess_count; /* Number of opened session */
47 struct malloc_stats heap;
48 };
49
50 static const char *stats_progname = "xtest --stats";
51
usage(void)52 static int usage(void)
53 {
54 fprintf(stderr, "Usage: %s [OPTION]\n", stats_progname);
55 fprintf(stderr, "Displays statistics from OP-TEE\n");
56 fprintf(stderr, "Options:\n");
57 fprintf(stderr, " -h|--help Print this help and exit\n");
58 fprintf(stderr, " --pager Print pager statistics\n");
59 fprintf(stderr, " --alloc Print allocation statistics\n");
60 fprintf(stderr, " --memleak Dump memory leak data on secure console\n");
61 fprintf(stderr, " --ta Print loaded TAs context\n");
62
63 return EXIT_FAILURE;
64 }
65
open_sess(TEEC_Context * ctx,TEEC_Session * sess)66 static void open_sess(TEEC_Context *ctx, TEEC_Session *sess)
67 {
68 TEEC_UUID uuid = STATS_UUID;
69 TEEC_Result res = TEEC_ERROR_GENERIC;
70 uint32_t eo = 0;
71
72 res = TEEC_InitializeContext(NULL, ctx);
73 if (res)
74 errx(EXIT_FAILURE, "TEEC_InitializeContext: %#"PRIx32, res);
75
76 res = TEEC_OpenSession(ctx, sess, &uuid, TEEC_LOGIN_PUBLIC, NULL,
77 NULL, &eo);
78 if (res)
79 errx(EXIT_FAILURE,
80 "TEEC_OpenSession: res %#"PRIx32" err_orig %#"PRIx32,
81 res, eo);
82 }
83
close_sess(TEEC_Context * ctx,TEEC_Session * sess)84 static int close_sess(TEEC_Context *ctx, TEEC_Session *sess)
85 {
86 TEEC_CloseSession(sess);
87 TEEC_FinalizeContext(ctx);
88
89 return EXIT_SUCCESS;
90 }
91
stat_pager(int argc,char * argv[])92 static int stat_pager(int argc, char *argv[])
93 {
94 TEEC_Context ctx = { };
95 TEEC_Session sess = { };
96 TEEC_Result res = TEEC_ERROR_GENERIC;
97 uint32_t eo = 0;
98 TEEC_Operation op = { };
99
100 UNUSED(argv);
101 if (argc != 1)
102 return usage();
103
104 open_sess(&ctx, &sess);
105
106 op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT, TEEC_VALUE_OUTPUT,
107 TEEC_VALUE_OUTPUT, TEEC_NONE);
108
109 res = TEEC_InvokeCommand(&sess, STATS_CMD_PAGER_STATS, &op, &eo);
110 if (res)
111 errx(EXIT_FAILURE,
112 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
113 res, eo);
114
115 printf("Pager statistics (Number of):\n");
116 printf("Unlocked pages: %"PRId32"\n", op.params[0].value.a);
117 printf("Page pool size: %"PRId32"\n", op.params[0].value.b);
118 printf("R/O faults: %"PRId32"\n", op.params[1].value.a);
119 printf("R/W faults: %"PRId32"\n", op.params[1].value.b);
120 printf("Hidden faults: %"PRId32"\n", op.params[2].value.a);
121 printf("Zi pages released: %"PRId32"\n", op.params[2].value.b);
122
123 return close_sess(&ctx, &sess);
124 }
125
stat_alloc(int argc,char * argv[])126 static int stat_alloc(int argc, char *argv[])
127 {
128 TEEC_Context ctx = { };
129 TEEC_Session sess = { };
130 TEEC_Result res = TEEC_ERROR_GENERIC;
131 uint32_t eo = 0;
132 TEEC_Operation op = { };
133 struct malloc_stats *stats = NULL;
134 size_t stats_size_bytes = 0;
135 size_t n = 0;
136
137 UNUSED(argv);
138 if (argc != 1)
139 return usage();
140
141 open_sess(&ctx, &sess);
142
143 op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
144 TEEC_MEMREF_TEMP_OUTPUT,
145 TEEC_NONE, TEEC_NONE);
146 res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
147 if (res != TEEC_ERROR_SHORT_BUFFER)
148 errx(EXIT_FAILURE,
149 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
150 res, eo);
151
152 stats_size_bytes = op.params[1].tmpref.size;
153 if (stats_size_bytes % sizeof(*stats))
154 errx(EXIT_FAILURE,
155 "STATS_CMD_PAGER_STATS: %zu not a multiple of %zu",
156 stats_size_bytes, sizeof(*stats));
157 stats = calloc(1, stats_size_bytes);
158 if (!stats)
159 err(EXIT_FAILURE, "calloc(1, %zu)", stats_size_bytes);
160
161 op.params[1].tmpref.buffer = stats;
162 op.params[1].tmpref.size = stats_size_bytes;
163 res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
164 if (res)
165 errx(EXIT_FAILURE,
166 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
167 res, eo);
168
169 if (op.params[1].tmpref.size != stats_size_bytes)
170 errx(EXIT_FAILURE,
171 "STATS_CMD_PAGER_STATS: expected size %zu, got %zu",
172 stats_size_bytes, op.params[1].tmpref.size);
173
174 for (n = 0; n < stats_size_bytes / sizeof(*stats); n++) {
175 if (n)
176 printf("\n");
177 printf("Pool: %*s\n",
178 (int)strnlen(stats[n].desc, sizeof(stats[n].desc)),
179 stats[n].desc);
180 printf("Bytes allocated: %"PRId32"\n",
181 stats[n].allocated);
182 printf("Max bytes allocated: %"PRId32"\n",
183 stats[n].max_allocated);
184 printf("Size of pool: %"PRId32"\n",
185 stats[n].size);
186 printf("Number of failed allocations: %"PRId32"\n",
187 stats[n].num_alloc_fail);
188 printf("Size of larges allocation failure: %"PRId32"\n",
189 stats[n].biggest_alloc_fail);
190 printf("Total bytes allocated at that failure: %"PRId32"\n",
191 stats[n].biggest_alloc_fail_used);
192 }
193
194 free(stats);
195
196 return close_sess(&ctx, &sess);
197 }
198
stat_memleak(int argc,char * argv[])199 static int stat_memleak(int argc, char *argv[])
200 {
201 TEEC_Context ctx = { };
202 TEEC_Session sess = { };
203 TEEC_Result res = TEEC_ERROR_GENERIC;
204 uint32_t eo = 0;
205
206 UNUSED(argv);
207 if (argc != 1)
208 return usage();
209
210 open_sess(&ctx, &sess);
211
212 res = TEEC_InvokeCommand(&sess, STATS_CMD_MEMLEAK_STATS, NULL, &eo);
213 if (res)
214 errx(EXIT_FAILURE,
215 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
216 res, eo);
217
218 return close_sess(&ctx, &sess);
219 }
220
stat_loaded_ta(int argc,char * argv[])221 static int stat_loaded_ta(int argc, char *argv[])
222 {
223 TEEC_Context ctx = { };
224 TEEC_Session sess = { };
225 TEEC_Result res = TEEC_ERROR_GENERIC;
226 uint32_t eo = 0;
227 TEEC_Operation op = { };
228 void *buff = NULL;
229 struct ta_dump_stats *stats = NULL;
230 size_t stats_size_bytes = 0;
231 size_t n = 0;
232 uint32_t retry_count = 10;
233
234 UNUSED(argv);
235 if (argc != 1)
236 return usage();
237
238 open_sess(&ctx, &sess);
239 retry:
240 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT,
241 TEEC_NONE, TEEC_NONE, TEEC_NONE);
242 res = TEEC_InvokeCommand(&sess, STATS_CMD_TA_STATS, &op, &eo);
243 if (res != TEEC_ERROR_SHORT_BUFFER)
244 errx(EXIT_FAILURE,
245 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
246 res, eo);
247
248 stats_size_bytes = op.params[0].tmpref.size;
249 if (stats_size_bytes == 0) {
250 printf("No loaded TA found");
251 goto out;
252 }
253
254 if (stats_size_bytes % sizeof(*stats))
255 errx(EXIT_FAILURE,
256 "STATS_CMD_TA_STATS: %zu not a multiple of %zu",
257 stats_size_bytes, sizeof(*stats));
258
259 /* Always allocate two more in case TA loaded after size querying */
260 stats_size_bytes += 2 * sizeof(*stats);
261
262 stats = realloc(buff, stats_size_bytes);
263 if (!stats)
264 errx(EXIT_FAILURE, "realloc(%zu) failed", stats_size_bytes);
265 buff = stats;
266
267 op.params[0].tmpref.buffer = stats;
268 op.params[0].tmpref.size = stats_size_bytes;
269 res = TEEC_InvokeCommand(&sess, STATS_CMD_TA_STATS, &op, &eo);
270 if (res == TEEC_ERROR_SHORT_BUFFER && retry_count > 0) {
271 retry_count--;
272 goto retry;
273 }
274
275 if (res)
276 errx(EXIT_FAILURE,
277 "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
278 res, eo);
279
280 for (n = 0; n < op.params[0].tmpref.size / sizeof(*stats); n++) {
281 if (n)
282 printf("\n");
283 printf("ta(%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x)\n",
284 stats[n].uuid.timeLow, stats[n].uuid.timeMid,
285 stats[n].uuid.timeHiAndVersion,
286 stats[n].uuid.clockSeqAndNode[0],
287 stats[n].uuid.clockSeqAndNode[1],
288 stats[n].uuid.clockSeqAndNode[2],
289 stats[n].uuid.clockSeqAndNode[3],
290 stats[n].uuid.clockSeqAndNode[4],
291 stats[n].uuid.clockSeqAndNode[5],
292 stats[n].uuid.clockSeqAndNode[6],
293 stats[n].uuid.clockSeqAndNode[7]);
294 printf("\tpanicked(%"PRId32") -- True if TA has panicked\n",
295 stats[n].panicked);
296 printf("\tsession number(%"PRId32")\n", stats[n].sess_count);
297 printf("\tHeap Status:\n");
298 printf("\t\tBytes allocated: %"PRId32"\n",
299 stats[n].heap.allocated);
300 printf("\t\tMax bytes allocated: %"PRId32"\n",
301 stats[n].heap.max_allocated);
302 printf("\t\tSize of pool: %"PRId32"\n",
303 stats[n].heap.size);
304 printf("\t\tNumber of failed allocations: %"PRId32"\n",
305 stats[n].heap.num_alloc_fail);
306 printf("\t\tSize of larges allocation failure: %"PRId32"\n",
307 stats[n].heap.biggest_alloc_fail);
308 printf("\t\tTotal bytes allocated at that failure: %"PRId32"\n",
309 stats[n].heap.biggest_alloc_fail_used);
310 }
311
312 out:
313 free(buff);
314 return close_sess(&ctx, &sess);
315 }
316
stats_runner_cmd_parser(int argc,char * argv[])317 int stats_runner_cmd_parser(int argc, char *argv[])
318 {
319 if (argc > 1) {
320 if (!strcmp(argv[1], "--pager"))
321 return stat_pager(argc - 1, argv + 1);
322 if (!strcmp(argv[1], "--alloc"))
323 return stat_alloc(argc - 1, argv + 1);
324 if (!strcmp(argv[1], "--memleak"))
325 return stat_memleak(argc - 1, argv + 1);
326 if (!strcmp(argv[1], "--ta"))
327 return stat_loaded_ta(argc - 1, argv + 1);
328 }
329
330 return usage();
331 }
332