1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #pragma once
25 #include <ctype.h>
26 #include <stdio.h>
27
28 #include <lib/unittest/unittest.h>
29
30 #include "trusty_bench_option_cb.h"
31
32 /**
33 * enum bench_aggregate_idx - The position of the calculated aggregate in the
34 * aggregate array of the bench_metric_node
35 * @BENCH_AGGREGATE_MIN: index of the current minimum value for this metric.
36 * @BENCH_AGGREGATE_MAX: index of the current maximum value for this metric.
37 * @BENCH_AGGREGATE_AVG: index of the current average value for this metric.
38 * @BENCH_AGGREGATE_COLD:index of the cold run value for this metric.
39 * @BENCH_NUM_AGGREGATE: Number of available aggregates. Indicates the end of
40 * the enum possible values.
41 */
42 enum bench_aggregate_idx {
43 BENCH_AGGREGATE_MIN = 0,
44 BENCH_AGGREGATE_MAX = 1,
45 BENCH_AGGREGATE_AVG = 2,
46 BENCH_AGGREGATE_COLD = 3,
47 BENCH_NUM_AGGREGATE = 4
48 };
49
50 /**
51 * struct bench_metric_node - holds current aggregate for the metrics of the
52 * current bench.
53 * @cnt: Number of BENCH runs already aggregated.
54 * @tot: Total of all values returned by BENCH_RESULT.
55 * @cold: Value of the metric for the initial cold run.
56 * @aggregates: Array of computed aggregates.
57 * BENCH_AGGREGATE_MIN: Smallest value returned by
58 * BENCH_RESULT. BENCH_AGGREGATE_MAX: Highest value returned by BENCH_RESULT.
59 * BENCH_AGGREGATE_AVG: Average value returned by
60 * BENCH_RESULT.
61 */
62 struct bench_metric_node {
63 size_t cnt;
64 int64_t tot;
65 int64_t cold;
66 int64_t aggregates[BENCH_NUM_AGGREGATE];
67 };
68
69 /**
70 * struct bench_metric_list_node - holds a metric declared by BENCH_RESULT in
71 * a lk_list node
72 * @node: List node.
73 * @metric: Metric values container.
74 * @name: Name to use in summary table header for this metric.
75 * @param_idx: index of current param in param array this metric node
76 * is aggregating for.
77 * @col_sz: size in bytes needed to print this column.
78 * @bench_result: Function pointer holding the BENCH_RESULT body
79 * Used to get the value to be aggregate for this metric
80 * after each BENCH body run.
81 * @formatted_value_cb: A callback of
82 * trusty_bench_get_formatted_value_callback_t type for
83 * formatting the result value to a string
84 * @param_name_cb: A callback of trusty_bench_get_param_name_callback_t
85 * type for formatting the param name
86 */
87 struct bench_metric_list_node {
88 struct list_node node;
89 struct bench_metric_node metric;
90 const char* name;
91 size_t param_idx;
92 size_t nb_params;
93 size_t col_sz;
94 int64_t (*bench_result)(void);
95 trusty_bench_get_formatted_value_callback_t formatted_value_cb;
96 trusty_bench_get_param_name_callback_t param_name_cb;
97 trusty_bench_check_results_callback_t check_results_cb;
98 };
99
100 /*
101 * Some Helper Functions for human readable output.
102 */
103
104 /* Some hardcoded sizes */
105 #define BENCH_MAX_COL_SIZE 64
106 #define BENCH_LEFTMOST_COL_SIZE 16
107 #define BENCH_TITLE_WIDTH 72
108
109 /* Total Width of the resulting horizontal Table */
110 static size_t trusty_bench_table_total_width;
111
112 /**
113 * trusty_bench_validate_numeric - Utility function to parse Google F1 SQL valid
114 * values except NaN and +/-inf
115 *
116 * @s: string to parse
117 *
118 * Return: true if s is a valid double
119 */
trusty_bench_validate_numeric(const char * s)120 static inline bool trusty_bench_validate_numeric(const char* s) {
121 bool found_dot = false;
122 /* ignore initital spaces */
123 while (*s != '\0' && (*s == ' ' || *s == '\t')) {
124 ++s;
125 }
126
127 /* can optionally start with sign */
128 if (*s == '+' || *s == '-') {
129 ++s;
130 }
131
132 /* Then digits and one optional dot */
133 while (*s != '\0' && *s != ' ' && *s != '\t') {
134 switch (*s) {
135 case '.':
136 if (found_dot) {
137 return false;
138 }
139 found_dot = true;
140 break;
141 case 'E':
142 case 'e':
143 found_dot = true; // dot are not allowed anymore
144
145 // Start Exponent. Ignore Sign.
146 ++s;
147 if (*s == '+' || *s == '-') {
148 ++s;
149 }
150 // Make sure there is an exponent after the E+/-.
151 // Let the loop do the increment
152 if (!isdigit(*s)) {
153 return false;
154 }
155 break;
156 default:
157 // Note: Leading 0 are accepted by SQL SAFE_CAST parsing functions
158 if (!isdigit(*s)) {
159 return false;
160 }
161 break;
162 }
163 ++s;
164 }
165
166 /* ignore trailing spaces */
167 while (*s != '\0' && (*s == ' ' || *s == '\t')) {
168 ++s;
169 }
170 if (*s == '\0') {
171 return true;
172 }
173
174 return false;
175 }
176
177 /**
178 * trusty_bench_sprint_col_stat - print the value of one statistical
179 * aggregate in a formatted column
180 * @buffer: Buffer in which to write the results. Preallocated.
181 * @buffer_len: Size of the Buffer in bytes.
182 * @val: Value to print
183 * @metric_name: Name of the metric for which this value is to be
184 * formatted
185 */
trusty_bench_sprint_col_stat(char * buffer,size_t buffer_len,int64_t val,const char * metric_name,trusty_bench_get_formatted_value_callback_t value_format_cb)186 static inline void trusty_bench_sprint_col_stat(
187 char* buffer,
188 size_t buffer_len,
189 int64_t val,
190 const char* metric_name,
191 trusty_bench_get_formatted_value_callback_t value_format_cb) {
192 if (value_format_cb == NULL) {
193 value_format_cb = trusty_bench_get_formatted_value_cb;
194 }
195
196 if (value_format_cb == NULL) {
197 snprintf(buffer, buffer_len, "%" PRId64, val);
198 } else {
199 value_format_cb(buffer, buffer_len, val, metric_name);
200 EXPECT_EQ(trusty_bench_validate_numeric(buffer), true,
201 "%s is not a valid double representation.\n", buffer);
202 }
203 }
204
205 /* Number of CPU on which to bench */
206 static uint8_t trusty_bench_nb_cpu = 1;
207 static size_t trusty_cur_bench_nb_params = 1;
trusty_bench_multi_cpu_param_idx(size_t param_idx)208 static size_t trusty_bench_multi_cpu_param_idx(size_t param_idx) {
209 return param_idx % trusty_cur_bench_nb_params;
210 }
trusty_bench_cpu_idx(size_t param_idx)211 static size_t trusty_bench_cpu_idx(size_t param_idx) {
212 return param_idx / trusty_cur_bench_nb_params;
213 }
214