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 <lib/unittest/unittest.h>
26 #include <trusty_log.h>
27
28 /* Max Width ever needed for a cell in the table in horizontal printing*/
29 static size_t trusty_bench_max_column_width;
30
31 /* Max Width ever needed for a metric cell in the table */
32 static size_t trusty_bench_max_metric_name_width =
33 6 /* strlen("Metric") is the minimum needed*/;
34
35 /* Max Width ever needed for a Param cell in the table */
36 static size_t trusty_bench_max_param_name_width =
37 5 /* strlen("Param") is the minimum needed*/;
38
39 /* Max Width ever needed for a Metric Value cell in the table */
40 static size_t trusty_bench_max_metric_digit_width;
41
reset_vertical_print_widths(void)42 static inline void reset_vertical_print_widths(void) {
43 /* strlen("Metric") is the minimum needed*/
44 trusty_bench_max_metric_name_width = 6;
45 /* strlen("Param") is the minimum needed*/
46 trusty_bench_max_param_name_width = 5;
47 /* Max of strlen of Min/Max/Avg/Cold is the minimum needed*/
48 trusty_bench_max_metric_digit_width = 4;
49 }
50
51 /**
52 * trusty_bench_print_border - Prints a Dash Sequence of prescribed size sz.
53 * @sz: Number of Dashes to be printed.
54 */
trusty_bench_print_border(size_t sz)55 static inline void trusty_bench_print_border(size_t sz) {
56 for (size_t i = 0; i < sz; ++i) {
57 trusty_unittest_printf("-");
58 }
59 trusty_unittest_printf("\n");
60 }
61
62 /**
63 * trusty_bench_center_print - Prints sz char in total, centering val inside it.
64 * If unbalanced, left is one space character smaller
65 * @sz: Size of the column.
66 * @val: String to print.
67 */
trusty_bench_center_print(size_t sz,const char * val)68 static inline void trusty_bench_center_print(size_t sz, const char* val) {
69 int spaces = (int)(sz - strlen(val));
70 int left = spaces / 2;
71 int right = spaces - left;
72
73 for (int i = 0; i < left; ++i) {
74 trusty_unittest_printf(" ");
75 }
76 trusty_unittest_printf("%s", val);
77 for (int i = 0; i < right; ++i) {
78 trusty_unittest_printf(" ");
79 }
80 }
81
82 /**
83 * trusty_bench_left_print - Prints sz char in total, val on the left of it.
84 * @sz: Size of the column.
85 * @val: String to print.
86 */
trusty_bench_left_print(size_t sz,const char * val)87 static inline void trusty_bench_left_print(size_t sz, const char* val) {
88 trusty_unittest_printf("%s%*s", val, (int)(sz - strlen(val)), "");
89 }
90
91 /**
92 * trusty_bench_print_title - Prints Benchmark Title in dashed box.
93 * @suite: Suite Name
94 * @bench: Bench Name
95 * @param: Param Name
96 */
trusty_bench_print_title(const char * suite,const char * bench,const char * param)97 static inline void trusty_bench_print_title(const char* suite,
98 const char* bench,
99 const char* param) {
100 char buffer[64];
101
102 int nb_chars =
103 snprintf(buffer, sizeof(buffer), "RUNNING %s_%s", suite, bench);
104 if (*param != '\0' && nb_chars < (int)sizeof(buffer)) {
105 snprintf(buffer + nb_chars, sizeof(buffer) - nb_chars, "_%s", param);
106 }
107 trusty_bench_print_border(BENCH_TITLE_WIDTH);
108 trusty_bench_center_print(BENCH_TITLE_WIDTH - 1, buffer);
109 trusty_unittest_printf("|\n");
110 trusty_bench_print_border(BENCH_TITLE_WIDTH);
111 }
112
113 /**
114 * trusty_bench_print_col_header - Prints column header with fixed size padding.
115 * @sz: Column Width
116 * @name: Title of the column
117 * @center: Center text in cell?
118 */
trusty_bench_print_col_header(size_t sz,const char * name,bool center)119 static inline void trusty_bench_print_col_header(size_t sz,
120 const char* name,
121 bool center) {
122 if (center) {
123 trusty_bench_center_print(sz, name);
124 } else {
125 trusty_bench_left_print(sz, name);
126 }
127 trusty_unittest_printf("|");
128 }
129
130 /**
131 * trusty_bench_print_header - Prints all column headers for a metric summary
132 * table.
133 * @metric_list: List of metrics aggregated during all BENCH runs
134 */
trusty_bench_print_header(struct list_node * metric_list)135 static inline void trusty_bench_print_header(struct list_node* metric_list) {
136 struct bench_metric_list_node* entry;
137
138 trusty_unittest_printf("|");
139 trusty_bench_print_col_header(BENCH_LEFTMOST_COL_SIZE, " Metrics ", true);
140 const char* prev_metric = NULL;
141 size_t sz = 0;
142
143 list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
144 node) {
145 if (prev_metric == NULL) {
146 prev_metric = entry->name;
147 }
148
149 if (strcmp(prev_metric, entry->name) != 0) {
150 trusty_bench_print_col_header(sz - 1, prev_metric, true);
151
152 sz = 0;
153 prev_metric = entry->name;
154 }
155
156 sz += entry->col_sz + 1;
157 }
158
159 trusty_bench_print_col_header(sz - 1, prev_metric, true);
160 trusty_unittest_printf("\n");
161 }
162
163 /**
164 * trusty_bench_compute_widths - Compute Columns Width and Total Width before
165 * printing anything.
166 * @metric_list: List of metrics aggregated during all BENCH runs.
167 * @nb_params: number of parameters in the parameter array.
168 */
trusty_bench_compute_widths(struct list_node * metric_list,size_t nb_params)169 static inline void trusty_bench_compute_widths(struct list_node* metric_list,
170 size_t nb_params) {
171 struct bench_metric_list_node* entry;
172
173 trusty_bench_table_total_width = 0;
174 list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
175 node) {
176 char buf[BENCH_MAX_COL_SIZE];
177
178 /* Get the size of the header */
179 /* First must be bigger than the size of the param header if any */
180 size_t column_width = 0;
181
182 if (nb_params > 1 || trusty_bench_nb_cpu > 1) {
183 if (entry->param_name_cb) {
184 entry->param_name_cb(buf, sizeof(buf),
185 entry->param_idx % entry->nb_params);
186 } else if (trusty_bench_get_param_name_cb) {
187 trusty_bench_get_param_name_cb(
188 buf, sizeof(buf), entry->param_idx % entry->nb_params);
189 } else {
190 snprintf(buf, sizeof(buf), "%zu",
191 entry->param_idx % entry->nb_params);
192 }
193 size_t param_name_width = strnlen(buf, sizeof(buf));
194
195 trusty_bench_max_param_name_width =
196 MAX(trusty_bench_max_param_name_width, param_name_width);
197 trusty_bench_max_column_width =
198 MAX(trusty_bench_max_column_width, param_name_width);
199 column_width = MAX(column_width, param_name_width);
200 }
201
202 /* Then must be bigger than the size of the metric header */
203 snprintf(buf, sizeof(buf), "%s", entry->name);
204 size_t metric_name_width = strnlen(buf, sizeof(buf));
205
206 trusty_bench_max_column_width =
207 MAX(trusty_bench_max_column_width, metric_name_width);
208 trusty_bench_max_metric_name_width =
209 MAX(trusty_bench_max_metric_name_width, metric_name_width);
210 column_width = MAX(column_width, metric_name_width);
211
212 /* Get the size of the max value */
213 trusty_bench_sprint_col_stat(
214 buf, sizeof(buf), entry->metric.aggregates[BENCH_AGGREGATE_MAX],
215 entry->name, entry->formatted_value_cb);
216 trusty_bench_max_column_width =
217 MAX(strnlen(buf, sizeof(buf)), trusty_bench_max_column_width);
218 trusty_bench_max_metric_digit_width = MAX(
219 trusty_bench_max_metric_digit_width, strnlen(buf, sizeof(buf)));
220
221 /* Get the size of the min value, because aggregates are signed */
222 trusty_bench_sprint_col_stat(
223 buf, sizeof(buf), entry->metric.aggregates[BENCH_AGGREGATE_MIN],
224 entry->name, entry->formatted_value_cb);
225 trusty_bench_max_column_width =
226 MAX(strnlen(buf, sizeof(buf)), trusty_bench_max_column_width);
227 trusty_bench_max_metric_digit_width = MAX(
228 trusty_bench_max_metric_digit_width, strnlen(buf, sizeof(buf)));
229 column_width = MAX(column_width, trusty_bench_max_metric_digit_width);
230
231 /* Check Column is not too big */
232 if (trusty_bench_max_column_width > BENCH_MAX_COL_SIZE) {
233 TLOGE("Column size cannot exceed BENCH_MAX_COL_SIZE: %d",
234 BENCH_MAX_COL_SIZE);
235 return;
236 }
237
238 /* Set the size of the column */
239 entry->col_sz = column_width;
240 trusty_bench_table_total_width += column_width + 1;
241 }
242 trusty_bench_table_total_width += BENCH_LEFTMOST_COL_SIZE + 2;
243 }
244
245 /**
246 * trusty_bench_print_params - Print all parameter column headers
247 * @metric_list: List of metrics aggregated during all BENCH runs
248 */
trusty_bench_print_params(struct list_node * metric_list)249 static inline void trusty_bench_print_params(struct list_node* metric_list) {
250 struct bench_metric_list_node* entry;
251
252 trusty_unittest_printf("|");
253 trusty_bench_print_col_header(BENCH_LEFTMOST_COL_SIZE, " Params ", true);
254 list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
255 node) {
256 char buf[BENCH_MAX_COL_SIZE];
257
258 if (entry->param_name_cb) {
259 entry->param_name_cb(buf, sizeof(buf),
260 entry->param_idx % entry->nb_params);
261 } else if (trusty_bench_get_param_name_cb) {
262 trusty_bench_get_param_name_cb(buf, sizeof(buf),
263 entry->param_idx % entry->nb_params);
264 } else {
265 snprintf(buf, sizeof(buf), "%zu",
266 entry->param_idx % entry->nb_params);
267 }
268 trusty_bench_print_col_header(entry->col_sz, buf, true);
269 }
270 trusty_unittest_printf("\n");
271 }
272
273 /**
274 * trusty_bench_print_col_stat - print the value of one statistical
275 * aggregate in a formatted column
276 * @sz: Columns Width
277 * @val: Value to print
278 * @metric_name: Metric for which the aggregate stat is to be printed.
279 */
trusty_bench_print_col_stat(size_t sz,int64_t val,const char * metric_name,trusty_bench_get_formatted_value_callback_t value_format_cb)280 static inline void trusty_bench_print_col_stat(
281 size_t sz,
282 int64_t val,
283 const char* metric_name,
284 trusty_bench_get_formatted_value_callback_t value_format_cb) {
285 if (value_format_cb == NULL) {
286 value_format_cb = trusty_bench_get_formatted_value_cb;
287 }
288 if (value_format_cb == NULL) {
289 trusty_unittest_printf("%*" PRId64 "|", (int)sz, val);
290 } else {
291 char buffer[32];
292
293 value_format_cb(buffer, sizeof(buffer), val, metric_name);
294 if (trusty_bench_validate_numeric(buffer)) {
295 trusty_unittest_printf("%*s|", (int)sz, buffer);
296 } else {
297 trusty_unittest_printf("%*s|", (int)sz, "");
298 }
299 }
300 }
301
302 /**
303 * trusty_bench_print_stat - print one list with the value of one
304 * statistical aggregate across all params/metric
305 * @metric_list: List of metrics aggregated during all BENCH runs
306 * @idx: Position of the aggregate in the aggregate array
307 * @aggregate_name: Name of the aggregate for the row header on the left
308 */
trusty_bench_print_stat(struct list_node * metric_list,enum bench_aggregate_idx idx,const char * aggregate_name)309 static inline void trusty_bench_print_stat(struct list_node* metric_list,
310 enum bench_aggregate_idx idx,
311 const char* aggregate_name) {
312 struct bench_metric_list_node* entry;
313
314 trusty_unittest_printf("|");
315 trusty_bench_print_col_header(BENCH_LEFTMOST_COL_SIZE, aggregate_name,
316 true);
317 list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
318 node) {
319 if (entry->metric.cnt == 0) {
320 trusty_bench_center_print(trusty_bench_max_metric_digit_width, "-");
321 trusty_unittest_printf("|");
322 } else if (idx == BENCH_AGGREGATE_COLD) {
323 trusty_bench_print_col_stat(entry->col_sz, entry->metric.cold,
324 entry->name, entry->formatted_value_cb);
325 } else {
326 trusty_bench_print_col_stat(entry->col_sz,
327 entry->metric.aggregates[idx],
328 entry->name, entry->formatted_value_cb);
329 }
330 }
331 trusty_unittest_printf("\n");
332 }
333
334 /**
335 * trusty_bench_print_horizontal_metric_list - Prints a summary table of all
336 * statistical aggregates for all param/metric in the last BENCH
337 * @metric_list: List of metrics aggregated during all BENCH runs.
338 * @nb_params: Number of Parameters in the param array of BENCH.
339 * @suite_name: Name of the Bench Suite
340 * @bench_name: Name of the Bench
341 */
trusty_bench_print_horizontal_metric_list(struct list_node * metric_list,size_t nb_params,const char * suite_name,const char * bench_name)342 static inline void trusty_bench_print_horizontal_metric_list(
343 struct list_node* metric_list,
344 size_t nb_params,
345 const char* suite_name,
346 const char* bench_name) {
347 trusty_bench_compute_widths(metric_list, nb_params);
348 trusty_bench_print_border(trusty_bench_table_total_width);
349 trusty_bench_print_header(metric_list);
350 trusty_bench_print_border(trusty_bench_table_total_width);
351 if (nb_params > 1 || trusty_bench_nb_cpu > 1) {
352 trusty_bench_print_params(metric_list);
353 trusty_bench_print_border(trusty_bench_table_total_width);
354 }
355
356 trusty_bench_print_stat(metric_list, BENCH_AGGREGATE_MIN, "min");
357 trusty_bench_print_stat(metric_list, BENCH_AGGREGATE_AVG, "avg");
358 trusty_bench_print_stat(metric_list, BENCH_AGGREGATE_MAX, "max");
359 trusty_bench_print_stat(metric_list, BENCH_AGGREGATE_COLD, "cold");
360
361 trusty_bench_print_border(trusty_bench_table_total_width);
362 }
363
364 /**
365 * trusty_bench_print_vertical_metric_list - Prints a summary table of all
366 * statistical aggregates for all param/metric in the last BENCH
367 * @metric_list: List of metrics aggregated during all BENCH runs.
368 * @nb_params: Number of Parameters in the param array of BENCH.
369 * @suite_name: Name of the Bench Suite
370 * @bench_name: Name of the Bench
371 */
trusty_bench_print_vertical_metric_list(struct list_node * metric_list,size_t nb_params,const char * suite_name,const char * bench_name)372 static inline void trusty_bench_print_vertical_metric_list(
373 struct list_node* metric_list,
374 size_t nb_params,
375 const char* suite_name,
376 const char* bench_name) {
377 struct bench_metric_list_node* entry;
378
379 trusty_bench_compute_widths(metric_list, nb_params);
380 size_t width = trusty_bench_max_metric_name_width +
381 4 * trusty_bench_max_metric_digit_width + 6;
382
383 /* Need one column for params? */
384 if (nb_params > 1 || trusty_bench_nb_cpu > 1) {
385 width += trusty_bench_max_param_name_width + 1;
386 }
387 trusty_bench_print_border(width);
388 trusty_unittest_printf("|");
389 trusty_bench_print_col_header(trusty_bench_max_metric_name_width, "Metric",
390 false);
391 if (nb_params > 1 || trusty_bench_nb_cpu > 1) {
392 trusty_bench_print_col_header(trusty_bench_max_param_name_width,
393 "Param", false);
394 }
395 trusty_bench_print_col_header(trusty_bench_max_metric_digit_width, "Min",
396 false);
397 trusty_bench_print_col_header(trusty_bench_max_metric_digit_width, "Avg",
398 false);
399 trusty_bench_print_col_header(trusty_bench_max_metric_digit_width, "Max",
400 false);
401 trusty_bench_print_col_header(trusty_bench_max_metric_digit_width, "Cold",
402 false);
403 trusty_unittest_printf("\n");
404
405 const char* prev_metric = "";
406
407 list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
408 node) {
409 if (strcmp(prev_metric, entry->name) != 0) {
410 prev_metric = entry->name;
411 trusty_bench_print_border(width);
412 }
413 trusty_unittest_printf("|");
414 trusty_bench_print_col_header(trusty_bench_max_metric_name_width,
415 entry->name, false);
416 if (nb_params > 1 || trusty_bench_nb_cpu > 1) {
417 char buf[BENCH_MAX_COL_SIZE];
418
419 if (entry->param_name_cb) {
420 entry->param_name_cb(buf, sizeof(buf),
421 entry->param_idx % entry->nb_params);
422 } else if (trusty_bench_get_param_name_cb) {
423 trusty_bench_get_param_name_cb(
424 buf, sizeof(buf), entry->param_idx % entry->nb_params);
425 } else {
426 snprintf(buf, sizeof(buf), "%zu",
427 entry->param_idx % entry->nb_params);
428 }
429 trusty_bench_print_col_header(trusty_bench_max_param_name_width,
430 buf, false);
431 }
432
433 if (entry->metric.cnt) {
434 trusty_bench_print_col_stat(
435 trusty_bench_max_metric_digit_width,
436 entry->metric.aggregates[BENCH_AGGREGATE_MIN], entry->name,
437 entry->formatted_value_cb);
438 trusty_bench_print_col_stat(
439 trusty_bench_max_metric_digit_width,
440 entry->metric.aggregates[BENCH_AGGREGATE_AVG], entry->name,
441 entry->formatted_value_cb);
442 trusty_bench_print_col_stat(
443 trusty_bench_max_metric_digit_width,
444 entry->metric.aggregates[BENCH_AGGREGATE_MAX], entry->name,
445 entry->formatted_value_cb);
446 trusty_bench_print_col_stat(trusty_bench_max_metric_digit_width,
447 entry->metric.cold, entry->name,
448 entry->formatted_value_cb);
449 } else {
450 for (int i = 0; i < 4; i++) {
451 trusty_bench_center_print(trusty_bench_max_metric_digit_width,
452 "-");
453 trusty_unittest_printf("|");
454 }
455 }
456 trusty_unittest_printf("\n");
457 }
458 trusty_bench_print_border(width);
459 }
460