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