xref: /dpdk/app/test-compress-perf/comp_perf_options_parse.c (revision 5ecb687a5698d2d8ec1f3b3b5a7a16bceca3e29c)
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 < MIN_COMPRESSED_BUF_SIZE) {
330 		RTE_LOG(ERR, USER1, "Segment size must be higher than %d\n",
331 			MIN_COMPRESSED_BUF_SIZE - 1);
332 		return -1;
333 	}
334 
335 	if (test_data->seg_sz > MAX_SEG_SIZE) {
336 		RTE_LOG(ERR, USER1, "Segment size must be lower than %d\n",
337 			MAX_SEG_SIZE + 1);
338 		return -1;
339 	}
340 
341 	return 0;
342 }
343 
344 static int
345 parse_max_num_sgl_segs(struct comp_test_data *test_data, const char *arg)
346 {
347 	int ret = parse_uint16_t(&test_data->max_sgl_segs, arg);
348 
349 	if (ret) {
350 		RTE_LOG(ERR, USER1,
351 			"Failed to parse max number of segments per mbuf chain\n");
352 		return -1;
353 	}
354 
355 	if (test_data->max_sgl_segs == 0) {
356 		RTE_LOG(ERR, USER1, "Max number of segments per mbuf chain "
357 			"must be higher than 0\n");
358 		return -1;
359 	}
360 
361 	return 0;
362 }
363 
364 static int
365 parse_window_sz(struct comp_test_data *test_data, const char *arg)
366 {
367 	int ret = parse_uint16_t((uint16_t *)&test_data->window_sz, arg);
368 
369 	if (ret) {
370 		RTE_LOG(ERR, USER1, "Failed to parse window size\n");
371 		return -1;
372 	}
373 
374 	return 0;
375 }
376 
377 static int
378 parse_driver_name(struct comp_test_data *test_data, const char *arg)
379 {
380 	if (strlen(arg) > (sizeof(test_data->driver_name) - 1))
381 		return -1;
382 
383 	strlcpy(test_data->driver_name, arg,
384 			sizeof(test_data->driver_name));
385 
386 	return 0;
387 }
388 
389 static int
390 parse_test_file(struct comp_test_data *test_data, const char *arg)
391 {
392 	if (strlen(arg) > (sizeof(test_data->input_file) - 1))
393 		return -1;
394 
395 	strlcpy(test_data->input_file, arg, sizeof(test_data->input_file));
396 
397 	return 0;
398 }
399 
400 static int
401 parse_op_type(struct comp_test_data *test_data, const char *arg)
402 {
403 	struct name_id_map optype_namemap[] = {
404 		{
405 			"comp",
406 			COMPRESS_ONLY
407 		},
408 		{
409 			"decomp",
410 			DECOMPRESS_ONLY
411 		},
412 		{
413 			"comp_and_decomp",
414 			COMPRESS_DECOMPRESS
415 		}
416 	};
417 
418 	int id = get_str_key_id_mapping(optype_namemap,
419 			RTE_DIM(optype_namemap), arg);
420 	if (id < 0) {
421 		RTE_LOG(ERR, USER1, "Invalid operation type specified\n");
422 		return -1;
423 	}
424 
425 	test_data->test_op = (enum comp_operation)id;
426 
427 	return 0;
428 }
429 
430 static int
431 parse_huffman_enc(struct comp_test_data *test_data, const char *arg)
432 {
433 	struct name_id_map huffman_namemap[] = {
434 		{
435 			"default",
436 			RTE_COMP_HUFFMAN_DEFAULT
437 		},
438 		{
439 			"fixed",
440 			RTE_COMP_HUFFMAN_FIXED
441 		},
442 		{
443 			"dynamic",
444 			RTE_COMP_HUFFMAN_DYNAMIC
445 		}
446 	};
447 
448 	int id = get_str_key_id_mapping(huffman_namemap,
449 			RTE_DIM(huffman_namemap), arg);
450 	if (id < 0) {
451 		RTE_LOG(ERR, USER1, "Invalid Huffmane encoding specified\n");
452 		return -1;
453 	}
454 
455 	test_data->huffman_enc = (enum rte_comp_huffman)id;
456 
457 	return 0;
458 }
459 
460 static int
461 parse_level(struct comp_test_data *test_data, const char *arg)
462 {
463 	int ret;
464 
465 	/*
466 	 * Try parsing the argument as a range, if it fails,
467 	 * arse it as a list
468 	 */
469 	if (parse_range(arg, &test_data->level.min, &test_data->level.max,
470 			&test_data->level.inc) < 0) {
471 		ret = parse_list(arg, test_data->level.list,
472 					&test_data->level.min,
473 					&test_data->level.max);
474 		if (ret < 0) {
475 			RTE_LOG(ERR, USER1,
476 				"Failed to parse compression level/s\n");
477 			return -1;
478 		}
479 		test_data->level.count = ret;
480 
481 		if (test_data->level.max > RTE_COMP_LEVEL_MAX) {
482 			RTE_LOG(ERR, USER1, "Level cannot be higher than %u\n",
483 					RTE_COMP_LEVEL_MAX);
484 			return -1;
485 		}
486 	}
487 
488 	return 0;
489 }
490 
491 typedef int (*option_parser_t)(struct comp_test_data *test_data,
492 		const char *arg);
493 
494 struct long_opt_parser {
495 	const char *lgopt_name;
496 	option_parser_t parser_fn;
497 
498 };
499 
500 static struct option lgopts[] = {
501 
502 	{ CPERF_DRIVER_NAME, required_argument, 0, 0 },
503 	{ CPERF_TEST_FILE, required_argument, 0, 0 },
504 	{ CPERF_SEG_SIZE, required_argument, 0, 0 },
505 	{ CPERF_BURST_SIZE, required_argument, 0, 0 },
506 	{ CPERF_EXTENDED_SIZE, required_argument, 0, 0 },
507 	{ CPERF_POOL_SIZE, required_argument, 0, 0 },
508 	{ CPERF_MAX_SGL_SEGS, required_argument, 0, 0},
509 	{ CPERF_NUM_ITER, required_argument, 0, 0 },
510 	{ CPERF_OPTYPE,	required_argument, 0, 0 },
511 	{ CPERF_HUFFMAN_ENC, required_argument, 0, 0 },
512 	{ CPERF_LEVEL, required_argument, 0, 0 },
513 	{ CPERF_WINDOW_SIZE, required_argument, 0, 0 },
514 	{ NULL, 0, 0, 0 }
515 };
516 static int
517 comp_perf_opts_parse_long(int opt_idx, struct comp_test_data *test_data)
518 {
519 	struct long_opt_parser parsermap[] = {
520 		{ CPERF_DRIVER_NAME,	parse_driver_name },
521 		{ CPERF_TEST_FILE,	parse_test_file },
522 		{ CPERF_SEG_SIZE,	parse_seg_sz },
523 		{ CPERF_BURST_SIZE,	parse_burst_sz },
524 		{ CPERF_EXTENDED_SIZE,	parse_extended_input_sz },
525 		{ CPERF_POOL_SIZE,	parse_pool_sz },
526 		{ CPERF_MAX_SGL_SEGS,	parse_max_num_sgl_segs },
527 		{ CPERF_NUM_ITER,	parse_num_iter },
528 		{ CPERF_OPTYPE,		parse_op_type },
529 		{ CPERF_HUFFMAN_ENC,	parse_huffman_enc },
530 		{ CPERF_LEVEL,		parse_level },
531 		{ CPERF_WINDOW_SIZE,	parse_window_sz },
532 	};
533 	unsigned int i;
534 
535 	for (i = 0; i < RTE_DIM(parsermap); i++) {
536 		if (strncmp(lgopts[opt_idx].name, parsermap[i].lgopt_name,
537 				strlen(lgopts[opt_idx].name)) == 0)
538 			return parsermap[i].parser_fn(test_data, optarg);
539 	}
540 
541 	return -EINVAL;
542 }
543 
544 int
545 comp_perf_options_parse(struct comp_test_data *test_data, int argc, char **argv)
546 {
547 	int opt, retval, opt_idx;
548 
549 	while ((opt = getopt_long(argc, argv, "h", lgopts, &opt_idx)) != EOF) {
550 		switch (opt) {
551 		case 'h':
552 			usage(argv[0]);
553 			rte_exit(EXIT_SUCCESS, "Displayed help\n");
554 			break;
555 		/* long options */
556 		case 0:
557 			retval = comp_perf_opts_parse_long(opt_idx, test_data);
558 			if (retval != 0)
559 				return retval;
560 
561 			break;
562 
563 		default:
564 			usage(argv[0]);
565 			return -EINVAL;
566 		}
567 	}
568 
569 	return 0;
570 }
571 
572 void
573 comp_perf_options_default(struct comp_test_data *test_data)
574 {
575 	test_data->cdev_id = -1;
576 	test_data->seg_sz = 2048;
577 	test_data->burst_sz = 32;
578 	test_data->pool_sz = 8192;
579 	test_data->max_sgl_segs = 16;
580 	test_data->num_iter = 10000;
581 	test_data->huffman_enc = RTE_COMP_HUFFMAN_DYNAMIC;
582 	test_data->test_op = COMPRESS_DECOMPRESS;
583 	test_data->window_sz = -1;
584 	test_data->level.min = 1;
585 	test_data->level.max = 9;
586 	test_data->level.inc = 1;
587 }
588 
589 int
590 comp_perf_options_check(struct comp_test_data *test_data)
591 {
592 	if (test_data->driver_name[0] == '\0') {
593 		RTE_LOG(ERR, USER1, "Driver name has to be set\n");
594 		return -1;
595 	}
596 
597 	if (test_data->input_file[0] == '\0') {
598 		RTE_LOG(ERR, USER1, "Input file name has to be set\n");
599 		return -1;
600 	}
601 
602 	return 0;
603 }
604