// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019, Linaro Limited */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xtest_helpers.h" #include "xtest_test.h" #include "stats.h" #define STATS_UUID { 0xd96a5b40, 0xe2c7, 0xb1af, \ { 0x87, 0x94, 0x10, 0x02, 0xa5, 0xd5, 0xc6, 0x1b } } #define STATS_CMD_PAGER_STATS 0 #define STATS_CMD_ALLOC_STATS 1 #define STATS_CMD_MEMLEAK_STATS 2 #define TEE_ALLOCATOR_DESC_LENGTH 32 struct malloc_stats { char desc[TEE_ALLOCATOR_DESC_LENGTH]; uint32_t allocated; /* Bytes currently allocated */ uint32_t max_allocated; /* Tracks max value of allocated */ uint32_t size; /* Total size for this allocator */ uint32_t num_alloc_fail; /* Number of failed alloc requests */ uint32_t biggest_alloc_fail; /* Size of biggest failed alloc */ uint32_t biggest_alloc_fail_used; /* Alloc bytes when above occurred */ }; static const char *stats_progname = "xtest --stats"; static int usage(void) { fprintf(stderr, "Usage: %s [OPTION]\n", stats_progname); fprintf(stderr, "Displays statistics from OP-TEE\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, " -h|--help Print this help and exit\n"); fprintf(stderr, " --pager Print pager statistics\n"); fprintf(stderr, " --alloc Print allocation statistics\n"); fprintf(stderr, " --memleak Dump memory leak data on secure console\n"); return EXIT_FAILURE; } static void open_sess(TEEC_Context *ctx, TEEC_Session *sess) { TEEC_UUID uuid = STATS_UUID; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eo = 0; res = TEEC_InitializeContext(NULL, ctx); if (res) errx(EXIT_FAILURE, "TEEC_InitializeContext: %#"PRIx32, res); res = TEEC_OpenSession(ctx, sess, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &eo); if (res) errx(EXIT_FAILURE, "TEEC_OpenSession: res %#"PRIx32" err_orig %#"PRIx32, res, eo); } static int close_sess(TEEC_Context *ctx, TEEC_Session *sess) { TEEC_CloseSession(sess); TEEC_FinalizeContext(ctx); return EXIT_SUCCESS; } static int stat_pager(int argc, char *argv[]) { TEEC_Context ctx = { }; TEEC_Session sess = { }; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eo = 0; TEEC_Operation op = { }; UNUSED(argv); if (argc != 1) return usage(); open_sess(&ctx, &sess); op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT, TEEC_VALUE_OUTPUT, TEEC_VALUE_OUTPUT, TEEC_NONE); res = TEEC_InvokeCommand(&sess, STATS_CMD_PAGER_STATS, &op, &eo); if (res) errx(EXIT_FAILURE, "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32, res, eo); printf("Pager statistics (Number of):\n"); printf("Unlocked pages: %"PRId32"\n", op.params[0].value.a); printf("Page pool size: %"PRId32"\n", op.params[0].value.b); printf("R/O faults: %"PRId32"\n", op.params[1].value.a); printf("R/W faults: %"PRId32"\n", op.params[1].value.b); printf("Hidden faults: %"PRId32"\n", op.params[2].value.a); printf("Zi pages released: %"PRId32"\n", op.params[2].value.b); return close_sess(&ctx, &sess); } static int stat_alloc(int argc, char *argv[]) { TEEC_Context ctx = { }; TEEC_Session sess = { }; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eo = 0; TEEC_Operation op = { }; struct malloc_stats *stats = NULL; size_t stats_size_bytes = 0; size_t n = 0; UNUSED(argv); if (argc != 1) return usage(); open_sess(&ctx, &sess); op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE); res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo); if (res != TEEC_ERROR_SHORT_BUFFER) errx(EXIT_FAILURE, "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32, res, eo); stats_size_bytes = op.params[1].tmpref.size; if (stats_size_bytes % sizeof(*stats)) errx(EXIT_FAILURE, "STATS_CMD_PAGER_STATS: %zu not a multiple of %zu", stats_size_bytes, sizeof(*stats)); stats = calloc(1, stats_size_bytes); if (!stats) err(EXIT_FAILURE, "calloc(1, %zu)", stats_size_bytes); op.params[1].tmpref.buffer = stats; op.params[1].tmpref.size = stats_size_bytes; res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo); if (res) errx(EXIT_FAILURE, "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32, res, eo); if (op.params[1].tmpref.size != stats_size_bytes) errx(EXIT_FAILURE, "STATS_CMD_PAGER_STATS: expected size %zu, got %zu", stats_size_bytes, op.params[1].tmpref.size); for (n = 0; n < stats_size_bytes / sizeof(*stats); n++) { if (n) printf("\n"); printf("Pool: %*s\n", (int)strnlen(stats[n].desc, sizeof(stats[n].desc)), stats[n].desc); printf("Bytes allocated: %"PRId32"\n", stats[n].allocated); printf("Max bytes allocated: %"PRId32"\n", stats[n].max_allocated); printf("Size of pool: %"PRId32"\n", stats[n].size); printf("Number of failed allocations: %"PRId32"\n", stats[n].num_alloc_fail); printf("Size of larges allocation failure: %"PRId32"\n", stats[n].biggest_alloc_fail); printf("Total bytes allocated at that failure: %"PRId32"\n", stats[n].biggest_alloc_fail_used); } free(stats); return close_sess(&ctx, &sess); } static int stat_memleak(int argc, char *argv[]) { TEEC_Context ctx = { }; TEEC_Session sess = { }; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eo = 0; UNUSED(argv); if (argc != 1) return usage(); open_sess(&ctx, &sess); res = TEEC_InvokeCommand(&sess, STATS_CMD_MEMLEAK_STATS, NULL, &eo); if (res) errx(EXIT_FAILURE, "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32, res, eo); return close_sess(&ctx, &sess); } int stats_runner_cmd_parser(int argc, char *argv[]) { if (argc > 1) { if (!strcmp(argv[1], "--pager")) return stat_pager(argc - 1, argv + 1); if (!strcmp(argv[1], "--alloc")) return stat_alloc(argc - 1, argv + 1); if (!strcmp(argv[1], "--memleak")) return stat_memleak(argc - 1, argv + 1); } return usage(); }