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