xtest: add --stats applet

Adds the --stats applet to interact with the pseudo TA "stats".

Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/Android.mk b/Android.mk
index 73d1fb7..1005ded 100644
--- a/Android.mk
+++ b/Android.mk
@@ -49,6 +49,7 @@
 	regression_8000.c \
 	regression_8100.c \
 	sha_perf.c \
+	stats.c \
 	xtest_helpers.c \
 	xtest_main.c \
 	xtest_test.c
diff --git a/host/xtest/CMakeLists.txt b/host/xtest/CMakeLists.txt
index eaacb5c..1f3a6f4 100644
--- a/host/xtest/CMakeLists.txt
+++ b/host/xtest/CMakeLists.txt
@@ -55,6 +55,7 @@
 	regression_8000.c
 	regression_8100.c
 	sha_perf.c
+	stats.c
 	xtest_helpers.c
 	xtest_main.c
 	xtest_test.c
diff --git a/host/xtest/Makefile b/host/xtest/Makefile
index e4e2881..7e152ef 100644
--- a/host/xtest/Makefile
+++ b/host/xtest/Makefile
@@ -65,6 +65,7 @@
 	regression_8000.c \
 	regression_8100.c \
 	sha_perf.c \
+	stats.c \
 	xtest_helpers.c \
 	xtest_main.c \
 	xtest_test.c
diff --git a/host/xtest/stats.c b/host/xtest/stats.c
new file mode 100644
index 0000000..e3f41ed
--- /dev/null
+++ b/host/xtest/stats.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <compiler.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tee_client_api.h>
+#include <unistd.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[] __unused)
+{
+	TEEC_Context ctx;
+	TEEC_Session sess;
+	TEEC_Result res = TEEC_ERROR_GENERIC;
+	uint32_t eo = 0;
+	TEEC_Operation op;
+
+	if (argc != 1)
+		return usage();
+
+	open_sess(&ctx, &sess);
+
+	memset(&op, 0, sizeof(op));
+	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("Available physical pages: %"PRId32"\n", op.params[0].value.a);
+	printf("Faults:                   %"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[] __unused)
+{
+	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;
+
+	if (argc != 1)
+		return usage();
+
+	open_sess(&ctx, &sess);
+
+	memset(&op, 0, sizeof(op));
+
+	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[] __unused)
+{
+	TEEC_Context ctx;
+	TEEC_Session sess;
+	TEEC_Result res = TEEC_ERROR_GENERIC;
+	uint32_t eo = 0;
+
+	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();
+}
diff --git a/host/xtest/stats.h b/host/xtest/stats.h
new file mode 100644
index 0000000..f48ca06
--- /dev/null
+++ b/host/xtest/stats.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Linaro Limited
+ *
+ */
+
+#ifndef STATS_H
+#define STATS_H
+
+int stats_runner_cmd_parser(int argc, char *argv[]);
+
+#endif /*STATS_H*/
diff --git a/host/xtest/xtest_main.c b/host/xtest/xtest_main.c
index f9cdbb1..f53b82b 100644
--- a/host/xtest/xtest_main.c
+++ b/host/xtest/xtest_main.c
@@ -33,6 +33,7 @@
 /* include here shandalone tests */
 #include "crypto_common.h"
 #include "install_ta.h"
+#include "stats.h"
 
 
 ADBG_SUITE_DEFINE(benchmark);
@@ -79,6 +80,7 @@
 #ifdef CFG_SECURE_DATA_PATH
 	printf("\t--sdp-basic [opts] Basic Secure Data Path test setup ('-h' for usage)\n");
 #endif
+	printf("\t--stats [opts]     Various statistics ('-h' for usage)\n");
 	printf("\n");
 }
 
@@ -125,6 +127,8 @@
 	else if (argc > 1 && !strcmp(argv[1], "--sdp-basic"))
 		return sdp_basic_runner_cmd_parser(argc-1, &argv[1]);
 #endif
+	else if (argc > 1 && !strcmp(argv[1], "--stats"))
+		return stats_runner_cmd_parser(argc - 1, &argv[1]);
 
 	while ((opt = getopt(argc, argv, "d:l:t:h")) != -1)
 		switch (opt) {