app/compress-perf: add --ptest option
[dpdk.git] / app / test-compress-perf / comp_perf_options_parse.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <getopt.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <inttypes.h>
10 #include <stdlib.h>
11 #include <errno.h>
12
13 #include <rte_string_fns.h>
14 #include <rte_comp.h>
15
16 #include "comp_perf_options.h"
17
18 #define CPERF_PTEST_TYPE        ("ptest")
19 #define CPERF_DRIVER_NAME       ("driver-name")
20 #define CPERF_TEST_FILE         ("input-file")
21 #define CPERF_SEG_SIZE          ("seg-sz")
22 #define CPERF_BURST_SIZE        ("burst-sz")
23 #define CPERF_EXTENDED_SIZE     ("extended-input-sz")
24 #define CPERF_POOL_SIZE         ("pool-sz")
25 #define CPERF_MAX_SGL_SEGS      ("max-num-sgl-segs")
26 #define CPERF_NUM_ITER          ("num-iter")
27 #define CPERF_OPTYPE            ("operation")
28 #define CPERF_HUFFMAN_ENC       ("huffman-enc")
29 #define CPERF_LEVEL             ("compress-level")
30 #define CPERF_WINDOW_SIZE       ("window-sz")
31
32 struct name_id_map {
33         const char *name;
34         uint32_t id;
35 };
36
37 static void
38 usage(char *progname)
39 {
40         printf("%s [EAL options] --\n"
41                 " --ptest benchmark / verify :"
42                 " --driver-name NAME: compress driver to use\n"
43                 " --input-file NAME: file to compress and decompress\n"
44                 " --extended-input-sz N: extend file data up to this size (default: no extension)\n"
45                 " --seg-sz N: size of segment to store the data (default: 2048)\n"
46                 " --burst-sz N: compress operation burst size\n"
47                 " --pool-sz N: mempool size for compress operations/mbufs\n"
48                 "               (default: 8192)\n"
49                 " --max-num-sgl-segs N: maximum number of segments for each mbuf\n"
50                 "               (default: 16)\n"
51                 " --num-iter N: number of times the file will be\n"
52                 "               compressed/decompressed (default: 10000)\n"
53                 " --operation [comp/decomp/comp_and_decomp]: perform test on\n"
54                 "               compression, decompression or both operations\n"
55                 " --huffman-enc [fixed/dynamic/default]: Huffman encoding\n"
56                 "               (default: dynamic)\n"
57                 " --compress-level N: compression level, which could be a single value, list or range\n"
58                 "               (default: range between 1 and 9)\n"
59                 " --window-sz N: base two log value of compression window size\n"
60                 "               (e.g.: 15 => 32k, default: max supported by PMD)\n"
61                 " -h: prints this help\n",
62                 progname);
63 }
64
65 static int
66 get_str_key_id_mapping(struct name_id_map *map, unsigned int map_len,
67                 const char *str_key)
68 {
69         unsigned int i;
70
71         for (i = 0; i < map_len; i++) {
72
73                 if (strcmp(str_key, map[i].name) == 0)
74                         return map[i].id;
75         }
76
77         return -1;
78 }
79
80 static int
81 parse_cperf_test_type(struct comp_test_data *test_data, const char *arg)
82 {
83         struct name_id_map cperftest_namemap[] = {
84                 {
85                         cperf_test_type_strs[CPERF_TEST_TYPE_BENCHMARK],
86                         CPERF_TEST_TYPE_BENCHMARK
87                 },
88                 {
89                         cperf_test_type_strs[CPERF_TEST_TYPE_VERIFY],
90                         CPERF_TEST_TYPE_VERIFY
91                 }
92         };
93
94         int id = get_str_key_id_mapping(
95                         (struct name_id_map *)cperftest_namemap,
96                         RTE_DIM(cperftest_namemap), arg);
97         if (id < 0) {
98                 RTE_LOG(ERR, USER1, "failed to parse test type");
99                 return -1;
100         }
101
102         test_data->test = (enum cperf_perf_test_type)id;
103
104         return 0;
105 }
106
107 static int
108 parse_uint32_t(uint32_t *value, const char *arg)
109 {
110         char *end = NULL;
111         unsigned long n = strtoul(arg, &end, 10);
112
113         if ((optarg[0] == '\0') || (end == NULL) || (*end != '\0'))
114                 return -1;
115
116         if (n > UINT32_MAX)
117                 return -ERANGE;
118
119         *value = (uint32_t) n;
120
121         return 0;
122 }
123
124 static int
125 parse_uint16_t(uint16_t *value, const char *arg)
126 {
127         uint32_t val = 0;
128         int ret = parse_uint32_t(&val, arg);
129
130         if (ret < 0)
131                 return ret;
132
133         if (val > UINT16_MAX)
134                 return -ERANGE;
135
136         *value = (uint16_t) val;
137
138         return 0;
139 }
140
141 static int
142 parse_range(const char *arg, uint8_t *min, uint8_t *max, uint8_t *inc)
143 {
144         char *token;
145         uint8_t number;
146
147         char *copy_arg = strdup(arg);
148
149         if (copy_arg == NULL)
150                 return -1;
151
152         errno = 0;
153         token = strtok(copy_arg, ":");
154
155         /* Parse minimum value */
156         if (token != NULL) {
157                 number = strtoul(token, NULL, 10);
158
159                 if (errno == EINVAL || errno == ERANGE)
160                         goto err_range;
161
162                 *min = number;
163         } else
164                 goto err_range;
165
166         token = strtok(NULL, ":");
167
168         /* Parse increment value */
169         if (token != NULL) {
170                 number = strtoul(token, NULL, 10);
171
172                 if (errno == EINVAL || errno == ERANGE ||
173                                 number == 0)
174                         goto err_range;
175
176                 *inc = number;
177         } else
178                 goto err_range;
179
180         token = strtok(NULL, ":");
181
182         /* Parse maximum value */
183         if (token != NULL) {
184                 number = strtoul(token, NULL, 10);
185
186                 if (errno == EINVAL || errno == ERANGE ||
187                                 number < *min)
188                         goto err_range;
189
190                 *max = number;
191         } else
192                 goto err_range;
193
194         if (strtok(NULL, ":") != NULL)
195                 goto err_range;
196
197         free(copy_arg);
198         return 0;
199
200 err_range:
201         free(copy_arg);
202         return -1;
203 }
204
205 static int
206 parse_list(const char *arg, uint8_t *list, uint8_t *min, uint8_t *max)
207 {
208         char *token;
209         uint32_t number;
210         uint8_t count = 0;
211         uint32_t temp_min;
212         uint32_t temp_max;
213
214         char *copy_arg = strdup(arg);
215
216         if (copy_arg == NULL)
217                 return -1;
218
219         errno = 0;
220         token = strtok(copy_arg, ",");
221
222         /* Parse first value */
223         if (token != NULL) {
224                 number = strtoul(token, NULL, 10);
225
226                 if (errno == EINVAL || errno == ERANGE)
227                         goto err_list;
228
229                 list[count++] = number;
230                 temp_min = number;
231                 temp_max = number;
232         } else
233                 goto err_list;
234
235         token = strtok(NULL, ",");
236
237         while (token != NULL) {
238                 if (count == MAX_LIST) {
239                         RTE_LOG(WARNING, USER1,
240                                 "Using only the first %u sizes\n",
241                                         MAX_LIST);
242                         break;
243                 }
244
245                 number = strtoul(token, NULL, 10);
246
247                 if (errno == EINVAL || errno == ERANGE)
248                         goto err_list;
249
250                 list[count++] = number;
251
252                 if (number < temp_min)
253                         temp_min = number;
254                 if (number > temp_max)
255                         temp_max = number;
256
257                 token = strtok(NULL, ",");
258         }
259
260         if (min)
261                 *min = temp_min;
262         if (max)
263                 *max = temp_max;
264
265         free(copy_arg);
266         return count;
267
268 err_list:
269         free(copy_arg);
270         return -1;
271 }
272
273 static int
274 parse_num_iter(struct comp_test_data *test_data, const char *arg)
275 {
276         int ret = parse_uint32_t(&test_data->num_iter, arg);
277
278         if (ret) {
279                 RTE_LOG(ERR, USER1, "Failed to parse total iteration count\n");
280                 return -1;
281         }
282
283         if (test_data->num_iter == 0) {
284                 RTE_LOG(ERR, USER1,
285                                 "Total number of iterations must be higher than 0\n");
286                 return -1;
287         }
288
289         return ret;
290 }
291
292 static int
293 parse_pool_sz(struct comp_test_data *test_data, const char *arg)
294 {
295         int ret = parse_uint32_t(&test_data->pool_sz, arg);
296
297         if (ret) {
298                 RTE_LOG(ERR, USER1, "Failed to parse pool size");
299                 return -1;
300         }
301
302         if (test_data->pool_sz == 0) {
303                 RTE_LOG(ERR, USER1, "Pool size must be higher than 0\n");
304                 return -1;
305         }
306
307         return ret;
308 }
309
310 static int
311 parse_burst_sz(struct comp_test_data *test_data, const char *arg)
312 {
313         int ret = parse_uint16_t(&test_data->burst_sz, arg);
314
315         if (ret) {
316                 RTE_LOG(ERR, USER1, "Failed to parse burst size/s\n");
317                 return -1;
318         }
319
320         if (test_data->burst_sz == 0) {
321                 RTE_LOG(ERR, USER1, "Burst size must be higher than 0\n");
322                 return -1;
323         }
324
325         return 0;
326 }
327
328 static int
329 parse_extended_input_sz(struct comp_test_data *test_data, const char *arg)
330 {
331         uint32_t tmp;
332         int ret = parse_uint32_t(&tmp, arg);
333
334         if (ret) {
335                 RTE_LOG(ERR, USER1, "Failed to parse extended input size\n");
336                 return -1;
337         }
338         test_data->input_data_sz = tmp;
339
340         if (tmp == 0) {
341                 RTE_LOG(ERR, USER1,
342                         "Extended file size must be higher than 0\n");
343                 return -1;
344         }
345         return 0;
346 }
347
348 static int
349 parse_seg_sz(struct comp_test_data *test_data, const char *arg)
350 {
351         int ret = parse_uint16_t(&test_data->seg_sz, arg);
352
353         if (ret) {
354                 RTE_LOG(ERR, USER1, "Failed to parse segment size\n");
355                 return -1;
356         }
357
358         if (test_data->seg_sz < MIN_COMPRESSED_BUF_SIZE) {
359                 RTE_LOG(ERR, USER1, "Segment size must be higher than %d\n",
360                         MIN_COMPRESSED_BUF_SIZE - 1);
361                 return -1;
362         }
363
364         if (test_data->seg_sz > MAX_SEG_SIZE) {
365                 RTE_LOG(ERR, USER1, "Segment size must be lower than %d\n",
366                         MAX_SEG_SIZE + 1);
367                 return -1;
368         }
369
370         return 0;
371 }
372
373 static int
374 parse_max_num_sgl_segs(struct comp_test_data *test_data, const char *arg)
375 {
376         int ret = parse_uint16_t(&test_data->max_sgl_segs, arg);
377
378         if (ret) {
379                 RTE_LOG(ERR, USER1,
380                         "Failed to parse max number of segments per mbuf chain\n");
381                 return -1;
382         }
383
384         if (test_data->max_sgl_segs == 0) {
385                 RTE_LOG(ERR, USER1, "Max number of segments per mbuf chain "
386                         "must be higher than 0\n");
387                 return -1;
388         }
389
390         return 0;
391 }
392
393 static int
394 parse_window_sz(struct comp_test_data *test_data, const char *arg)
395 {
396         uint16_t tmp;
397         int ret = parse_uint16_t(&tmp, arg);
398
399         if (ret) {
400                 RTE_LOG(ERR, USER1, "Failed to parse window size\n");
401                 return -1;
402         }
403         test_data->window_sz = (int)tmp;
404
405         return 0;
406 }
407
408 static int
409 parse_driver_name(struct comp_test_data *test_data, const char *arg)
410 {
411         if (strlen(arg) > (sizeof(test_data->driver_name) - 1))
412                 return -1;
413
414         strlcpy(test_data->driver_name, arg,
415                         sizeof(test_data->driver_name));
416
417         return 0;
418 }
419
420 static int
421 parse_test_file(struct comp_test_data *test_data, const char *arg)
422 {
423         if (strlen(arg) > (sizeof(test_data->input_file) - 1))
424                 return -1;
425
426         strlcpy(test_data->input_file, arg, sizeof(test_data->input_file));
427
428         return 0;
429 }
430
431 static int
432 parse_op_type(struct comp_test_data *test_data, const char *arg)
433 {
434         struct name_id_map optype_namemap[] = {
435                 {
436                         "comp",
437                         COMPRESS_ONLY
438                 },
439                 {
440                         "decomp",
441                         DECOMPRESS_ONLY
442                 },
443                 {
444                         "comp_and_decomp",
445                         COMPRESS_DECOMPRESS
446                 }
447         };
448
449         int id = get_str_key_id_mapping(optype_namemap,
450                         RTE_DIM(optype_namemap), arg);
451         if (id < 0) {
452                 RTE_LOG(ERR, USER1, "Invalid operation type specified\n");
453                 return -1;
454         }
455
456         test_data->test_op = (enum comp_operation)id;
457
458         return 0;
459 }
460
461 static int
462 parse_huffman_enc(struct comp_test_data *test_data, const char *arg)
463 {
464         struct name_id_map huffman_namemap[] = {
465                 {
466                         "default",
467                         RTE_COMP_HUFFMAN_DEFAULT
468                 },
469                 {
470                         "fixed",
471                         RTE_COMP_HUFFMAN_FIXED
472                 },
473                 {
474                         "dynamic",
475                         RTE_COMP_HUFFMAN_DYNAMIC
476                 }
477         };
478
479         int id = get_str_key_id_mapping(huffman_namemap,
480                         RTE_DIM(huffman_namemap), arg);
481         if (id < 0) {
482                 RTE_LOG(ERR, USER1, "Invalid Huffmane encoding specified\n");
483                 return -1;
484         }
485
486         test_data->huffman_enc = (enum rte_comp_huffman)id;
487
488         return 0;
489 }
490
491 static int
492 parse_level(struct comp_test_data *test_data, const char *arg)
493 {
494         int ret;
495
496         /*
497          * Try parsing the argument as a range, if it fails,
498          * arse it as a list
499          */
500         if (parse_range(arg, &test_data->level_lst.min,
501                         &test_data->level_lst.max,
502                         &test_data->level_lst.inc) < 0) {
503                 ret = parse_list(arg, test_data->level_lst.list,
504                                         &test_data->level_lst.min,
505                                         &test_data->level_lst.max);
506                 if (ret < 0) {
507                         RTE_LOG(ERR, USER1,
508                                 "Failed to parse compression level/s\n");
509                         return -1;
510                 }
511                 test_data->level_lst.count = ret;
512
513                 if (test_data->level_lst.max > RTE_COMP_LEVEL_MAX) {
514                         RTE_LOG(ERR, USER1, "Level cannot be higher than %u\n",
515                                         RTE_COMP_LEVEL_MAX);
516                         return -1;
517                 }
518         }
519
520         return 0;
521 }
522
523 typedef int (*option_parser_t)(struct comp_test_data *test_data,
524                 const char *arg);
525
526 struct long_opt_parser {
527         const char *lgopt_name;
528         option_parser_t parser_fn;
529
530 };
531
532 static struct option lgopts[] = {
533
534         { CPERF_PTEST_TYPE, required_argument, 0, 0 },
535         { CPERF_DRIVER_NAME, required_argument, 0, 0 },
536         { CPERF_TEST_FILE, required_argument, 0, 0 },
537         { CPERF_SEG_SIZE, required_argument, 0, 0 },
538         { CPERF_BURST_SIZE, required_argument, 0, 0 },
539         { CPERF_EXTENDED_SIZE, required_argument, 0, 0 },
540         { CPERF_POOL_SIZE, required_argument, 0, 0 },
541         { CPERF_MAX_SGL_SEGS, required_argument, 0, 0},
542         { CPERF_NUM_ITER, required_argument, 0, 0 },
543         { CPERF_OPTYPE, required_argument, 0, 0 },
544         { CPERF_HUFFMAN_ENC, required_argument, 0, 0 },
545         { CPERF_LEVEL, required_argument, 0, 0 },
546         { CPERF_WINDOW_SIZE, required_argument, 0, 0 },
547         { NULL, 0, 0, 0 }
548 };
549 static int
550 comp_perf_opts_parse_long(int opt_idx, struct comp_test_data *test_data)
551 {
552         struct long_opt_parser parsermap[] = {
553                 { CPERF_PTEST_TYPE,     parse_cperf_test_type },
554                 { CPERF_DRIVER_NAME,    parse_driver_name },
555                 { CPERF_TEST_FILE,      parse_test_file },
556                 { CPERF_SEG_SIZE,       parse_seg_sz },
557                 { CPERF_BURST_SIZE,     parse_burst_sz },
558                 { CPERF_EXTENDED_SIZE,  parse_extended_input_sz },
559                 { CPERF_POOL_SIZE,      parse_pool_sz },
560                 { CPERF_MAX_SGL_SEGS,   parse_max_num_sgl_segs },
561                 { CPERF_NUM_ITER,       parse_num_iter },
562                 { CPERF_OPTYPE,         parse_op_type },
563                 { CPERF_HUFFMAN_ENC,    parse_huffman_enc },
564                 { CPERF_LEVEL,          parse_level },
565                 { CPERF_WINDOW_SIZE,    parse_window_sz },
566         };
567         unsigned int i;
568
569         for (i = 0; i < RTE_DIM(parsermap); i++) {
570                 if (strncmp(lgopts[opt_idx].name, parsermap[i].lgopt_name,
571                                 strlen(lgopts[opt_idx].name)) == 0)
572                         return parsermap[i].parser_fn(test_data, optarg);
573         }
574
575         return -EINVAL;
576 }
577
578 int
579 comp_perf_options_parse(struct comp_test_data *test_data, int argc, char **argv)
580 {
581         int opt, retval, opt_idx;
582
583         while ((opt = getopt_long(argc, argv, "h", lgopts, &opt_idx)) != EOF) {
584                 switch (opt) {
585                 case 'h':
586                         usage(argv[0]);
587                         rte_exit(EXIT_SUCCESS, "Displayed help\n");
588                         break;
589                 /* long options */
590                 case 0:
591                         retval = comp_perf_opts_parse_long(opt_idx, test_data);
592                         if (retval != 0)
593                                 return retval;
594
595                         break;
596
597                 default:
598                         usage(argv[0]);
599                         return -EINVAL;
600                 }
601         }
602
603         return 0;
604 }
605
606 void
607 comp_perf_options_default(struct comp_test_data *test_data)
608 {
609         test_data->seg_sz = 2048;
610         test_data->burst_sz = 32;
611         test_data->pool_sz = 8192;
612         test_data->max_sgl_segs = 16;
613         test_data->num_iter = 10000;
614         test_data->huffman_enc = RTE_COMP_HUFFMAN_DYNAMIC;
615         test_data->test_op = COMPRESS_DECOMPRESS;
616         test_data->window_sz = -1;
617         test_data->level_lst.min = 1;
618         test_data->level_lst.max = 9;
619         test_data->level_lst.inc = 1;
620         test_data->test = CPERF_TEST_TYPE_BENCHMARK;
621 }
622
623 int
624 comp_perf_options_check(struct comp_test_data *test_data)
625 {
626         if (test_data->driver_name[0] == '\0') {
627                 RTE_LOG(ERR, USER1, "Driver name has to be set\n");
628                 return -1;
629         }
630
631         if (test_data->input_file[0] == '\0') {
632                 RTE_LOG(ERR, USER1, "Input file name has to be set\n");
633                 return -1;
634         }
635
636         return 0;
637 }