File Index
Files and directories
Sign in
FILE

qstats.c

File preview with syntax highlighting.

linux-6.19.14/tools/net/ynl/ynltool/qstats.c C
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6#include <errno.h>
  7#include <net/if.h>
  8#include <math.h>
  9
 10#include <ynl.h>
 11#include "netdev-user.h"
 12
 13#include "main.h"
 14
 15static enum netdev_qstats_scope scope; /* default - device */
 16
 17struct queue_balance {
 18	unsigned int ifindex;
 19	enum netdev_queue_type type;
 20	unsigned int queue_count;
 21	__u64 *rx_packets;
 22	__u64 *rx_bytes;
 23	__u64 *tx_packets;
 24	__u64 *tx_bytes;
 25};
 26
 27static void print_json_qstats(struct netdev_qstats_get_list *qstats)
 28{
 29	jsonw_start_array(json_wtr);
 30
 31	ynl_dump_foreach(qstats, qs) {
 32		char ifname[IF_NAMESIZE];
 33		const char *name;
 34
 35		jsonw_start_object(json_wtr);
 36
 37		name = if_indextoname(qs->ifindex, ifname);
 38		if (name)
 39			jsonw_string_field(json_wtr, "ifname", name);
 40		jsonw_uint_field(json_wtr, "ifindex", qs->ifindex);
 41
 42		if (qs->_present.queue_type)
 43			jsonw_string_field(json_wtr, "queue-type",
 44					   netdev_queue_type_str(qs->queue_type));
 45		if (qs->_present.queue_id)
 46			jsonw_uint_field(json_wtr, "queue-id", qs->queue_id);
 47
 48		if (qs->_present.rx_packets || qs->_present.rx_bytes ||
 49		    qs->_present.rx_alloc_fail || qs->_present.rx_hw_drops ||
 50		    qs->_present.rx_csum_complete || qs->_present.rx_hw_gro_packets) {
 51			jsonw_name(json_wtr, "rx");
 52			jsonw_start_object(json_wtr);
 53			if (qs->_present.rx_packets)
 54				jsonw_uint_field(json_wtr, "packets", qs->rx_packets);
 55			if (qs->_present.rx_bytes)
 56				jsonw_uint_field(json_wtr, "bytes", qs->rx_bytes);
 57			if (qs->_present.rx_alloc_fail)
 58				jsonw_uint_field(json_wtr, "alloc-fail", qs->rx_alloc_fail);
 59			if (qs->_present.rx_hw_drops)
 60				jsonw_uint_field(json_wtr, "hw-drops", qs->rx_hw_drops);
 61			if (qs->_present.rx_hw_drop_overruns)
 62				jsonw_uint_field(json_wtr, "hw-drop-overruns", qs->rx_hw_drop_overruns);
 63			if (qs->_present.rx_hw_drop_ratelimits)
 64				jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->rx_hw_drop_ratelimits);
 65			if (qs->_present.rx_csum_complete)
 66				jsonw_uint_field(json_wtr, "csum-complete", qs->rx_csum_complete);
 67			if (qs->_present.rx_csum_unnecessary)
 68				jsonw_uint_field(json_wtr, "csum-unnecessary", qs->rx_csum_unnecessary);
 69			if (qs->_present.rx_csum_none)
 70				jsonw_uint_field(json_wtr, "csum-none", qs->rx_csum_none);
 71			if (qs->_present.rx_csum_bad)
 72				jsonw_uint_field(json_wtr, "csum-bad", qs->rx_csum_bad);
 73			if (qs->_present.rx_hw_gro_packets)
 74				jsonw_uint_field(json_wtr, "hw-gro-packets", qs->rx_hw_gro_packets);
 75			if (qs->_present.rx_hw_gro_bytes)
 76				jsonw_uint_field(json_wtr, "hw-gro-bytes", qs->rx_hw_gro_bytes);
 77			if (qs->_present.rx_hw_gro_wire_packets)
 78				jsonw_uint_field(json_wtr, "hw-gro-wire-packets", qs->rx_hw_gro_wire_packets);
 79			if (qs->_present.rx_hw_gro_wire_bytes)
 80				jsonw_uint_field(json_wtr, "hw-gro-wire-bytes", qs->rx_hw_gro_wire_bytes);
 81			jsonw_end_object(json_wtr);
 82		}
 83
 84		if (qs->_present.tx_packets || qs->_present.tx_bytes ||
 85		    qs->_present.tx_hw_drops || qs->_present.tx_csum_none ||
 86		    qs->_present.tx_hw_gso_packets) {
 87			jsonw_name(json_wtr, "tx");
 88			jsonw_start_object(json_wtr);
 89			if (qs->_present.tx_packets)
 90				jsonw_uint_field(json_wtr, "packets", qs->tx_packets);
 91			if (qs->_present.tx_bytes)
 92				jsonw_uint_field(json_wtr, "bytes", qs->tx_bytes);
 93			if (qs->_present.tx_hw_drops)
 94				jsonw_uint_field(json_wtr, "hw-drops", qs->tx_hw_drops);
 95			if (qs->_present.tx_hw_drop_errors)
 96				jsonw_uint_field(json_wtr, "hw-drop-errors", qs->tx_hw_drop_errors);
 97			if (qs->_present.tx_hw_drop_ratelimits)
 98				jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->tx_hw_drop_ratelimits);
 99			if (qs->_present.tx_csum_none)
100				jsonw_uint_field(json_wtr, "csum-none", qs->tx_csum_none);
101			if (qs->_present.tx_needs_csum)
102				jsonw_uint_field(json_wtr, "needs-csum", qs->tx_needs_csum);
103			if (qs->_present.tx_hw_gso_packets)
104				jsonw_uint_field(json_wtr, "hw-gso-packets", qs->tx_hw_gso_packets);
105			if (qs->_present.tx_hw_gso_bytes)
106				jsonw_uint_field(json_wtr, "hw-gso-bytes", qs->tx_hw_gso_bytes);
107			if (qs->_present.tx_hw_gso_wire_packets)
108				jsonw_uint_field(json_wtr, "hw-gso-wire-packets", qs->tx_hw_gso_wire_packets);
109			if (qs->_present.tx_hw_gso_wire_bytes)
110				jsonw_uint_field(json_wtr, "hw-gso-wire-bytes", qs->tx_hw_gso_wire_bytes);
111			if (qs->_present.tx_stop)
112				jsonw_uint_field(json_wtr, "stop", qs->tx_stop);
113			if (qs->_present.tx_wake)
114				jsonw_uint_field(json_wtr, "wake", qs->tx_wake);
115			jsonw_end_object(json_wtr);
116		}
117
118		jsonw_end_object(json_wtr);
119	}
120
121	jsonw_end_array(json_wtr);
122}
123
124static void print_one(bool present, const char *name, unsigned long long val,
125		      int *line)
126{
127	if (!present)
128		return;
129
130	if (!*line) {
131		printf("              ");
132		++(*line);
133	}
134
135	/* Don't waste space on tx- and rx- prefix, its implied by queue type */
136	if (scope == NETDEV_QSTATS_SCOPE_QUEUE &&
137	    (name[0] == 'r' || name[0] == 't') &&
138	    name[1] == 'x' && name[2] == '-')
139		name += 3;
140
141	printf(" %15s: %15llu", name, val);
142
143	if (++(*line) == 3) {
144		printf("\n");
145		*line = 0;
146	}
147}
148
149static void print_plain_qstats(struct netdev_qstats_get_list *qstats)
150{
151	ynl_dump_foreach(qstats, qs) {
152		char ifname[IF_NAMESIZE];
153		const char *name;
154		int n;
155
156		name = if_indextoname(qs->ifindex, ifname);
157		if (name)
158			printf("%s", name);
159		else
160			printf("ifindex:%u", qs->ifindex);
161
162		if (qs->_present.queue_type && qs->_present.queue_id)
163			printf("\t%s-%-3u",
164			       netdev_queue_type_str(qs->queue_type),
165			       qs->queue_id);
166		else
167			printf("\t      ");
168
169		n = 1;
170
171		/* Basic counters */
172		print_one(qs->_present.rx_packets, "rx-packets", qs->rx_packets, &n);
173		print_one(qs->_present.rx_bytes, "rx-bytes", qs->rx_bytes, &n);
174		print_one(qs->_present.tx_packets, "tx-packets", qs->tx_packets, &n);
175		print_one(qs->_present.tx_bytes, "tx-bytes", qs->tx_bytes, &n);
176
177		/* RX error/drop counters */
178		print_one(qs->_present.rx_alloc_fail, "rx-alloc-fail",
179			  qs->rx_alloc_fail, &n);
180		print_one(qs->_present.rx_hw_drops, "rx-hw-drops",
181			  qs->rx_hw_drops, &n);
182		print_one(qs->_present.rx_hw_drop_overruns, "rx-hw-drop-overruns",
183			  qs->rx_hw_drop_overruns, &n);
184		print_one(qs->_present.rx_hw_drop_ratelimits, "rx-hw-drop-ratelimits",
185			  qs->rx_hw_drop_ratelimits, &n);
186
187		/* RX checksum counters */
188		print_one(qs->_present.rx_csum_complete, "rx-csum-complete",
189			  qs->rx_csum_complete, &n);
190		print_one(qs->_present.rx_csum_unnecessary, "rx-csum-unnecessary",
191			  qs->rx_csum_unnecessary, &n);
192		print_one(qs->_present.rx_csum_none, "rx-csum-none",
193			  qs->rx_csum_none, &n);
194		print_one(qs->_present.rx_csum_bad, "rx-csum-bad",
195			  qs->rx_csum_bad, &n);
196
197		/* RX GRO counters */
198		print_one(qs->_present.rx_hw_gro_packets, "rx-hw-gro-packets",
199			  qs->rx_hw_gro_packets, &n);
200		print_one(qs->_present.rx_hw_gro_bytes, "rx-hw-gro-bytes",
201			  qs->rx_hw_gro_bytes, &n);
202		print_one(qs->_present.rx_hw_gro_wire_packets, "rx-hw-gro-wire-packets",
203			  qs->rx_hw_gro_wire_packets, &n);
204		print_one(qs->_present.rx_hw_gro_wire_bytes, "rx-hw-gro-wire-bytes",
205			  qs->rx_hw_gro_wire_bytes, &n);
206
207		/* TX error/drop counters */
208		print_one(qs->_present.tx_hw_drops, "tx-hw-drops",
209			  qs->tx_hw_drops, &n);
210		print_one(qs->_present.tx_hw_drop_errors, "tx-hw-drop-errors",
211			  qs->tx_hw_drop_errors, &n);
212		print_one(qs->_present.tx_hw_drop_ratelimits, "tx-hw-drop-ratelimits",
213			  qs->tx_hw_drop_ratelimits, &n);
214
215		/* TX checksum counters */
216		print_one(qs->_present.tx_csum_none, "tx-csum-none",
217			  qs->tx_csum_none, &n);
218		print_one(qs->_present.tx_needs_csum, "tx-needs-csum",
219			  qs->tx_needs_csum, &n);
220
221		/* TX GSO counters */
222		print_one(qs->_present.tx_hw_gso_packets, "tx-hw-gso-packets",
223			  qs->tx_hw_gso_packets, &n);
224		print_one(qs->_present.tx_hw_gso_bytes, "tx-hw-gso-bytes",
225			  qs->tx_hw_gso_bytes, &n);
226		print_one(qs->_present.tx_hw_gso_wire_packets, "tx-hw-gso-wire-packets",
227			  qs->tx_hw_gso_wire_packets, &n);
228		print_one(qs->_present.tx_hw_gso_wire_bytes, "tx-hw-gso-wire-bytes",
229			  qs->tx_hw_gso_wire_bytes, &n);
230
231		/* TX queue control */
232		print_one(qs->_present.tx_stop, "tx-stop", qs->tx_stop, &n);
233		print_one(qs->_present.tx_wake, "tx-wake", qs->tx_wake, &n);
234
235		if (n)
236			printf("\n");
237	}
238}
239
240static int do_show(int argc, char **argv)
241{
242	struct netdev_qstats_get_list *qstats;
243	struct netdev_qstats_get_req *req;
244	struct ynl_error yerr;
245	struct ynl_sock *ys;
246	int ret = 0;
247
248	/* Parse options */
249	while (argc > 0) {
250		if (is_prefix(*argv, "scope") || is_prefix(*argv, "group-by")) {
251			NEXT_ARG();
252
253			if (!REQ_ARGS(1))
254				return -1;
255
256			if (is_prefix(*argv, "queue")) {
257				scope = NETDEV_QSTATS_SCOPE_QUEUE;
258			} else if (is_prefix(*argv, "device")) {
259				scope = 0;
260			} else {
261				p_err("invalid scope value '%s'", *argv);
262				return -1;
263			}
264			NEXT_ARG();
265		} else {
266			p_err("unknown option '%s'", *argv);
267			return -1;
268		}
269	}
270
271	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
272	if (!ys) {
273		p_err("YNL: %s", yerr.msg);
274		return -1;
275	}
276
277	req = netdev_qstats_get_req_alloc();
278	if (!req) {
279		p_err("failed to allocate qstats request");
280		ret = -1;
281		goto exit_close;
282	}
283
284	if (scope)
285		netdev_qstats_get_req_set_scope(req, scope);
286
287	qstats = netdev_qstats_get_dump(ys, req);
288	netdev_qstats_get_req_free(req);
289	if (!qstats) {
290		p_err("failed to get queue stats: %s", ys->err.msg);
291		ret = -1;
292		goto exit_close;
293	}
294
295	/* Print the stats as returned by the kernel */
296	if (json_output)
297		print_json_qstats(qstats);
298	else
299		print_plain_qstats(qstats);
300
301	netdev_qstats_get_list_free(qstats);
302exit_close:
303	ynl_sock_destroy(ys);
304	return ret;
305}
306
307static void compute_stats(__u64 *values, unsigned int count,
308			  double *mean, double *stddev, __u64 *min, __u64 *max)
309{
310	double sum = 0.0, variance = 0.0;
311	unsigned int i;
312
313	*min = ~0ULL;
314	*max = 0;
315
316	if (count == 0) {
317		*mean = 0;
318		*stddev = 0;
319		*min = 0;
320		return;
321	}
322
323	for (i = 0; i < count; i++) {
324		sum += values[i];
325		if (values[i] < *min)
326			*min = values[i];
327		if (values[i] > *max)
328			*max = values[i];
329	}
330
331	*mean = sum / count;
332
333	if (count > 1) {
334		for (i = 0; i < count; i++) {
335			double diff = values[i] - *mean;
336
337			variance += diff * diff;
338		}
339		*stddev = sqrt(variance / (count - 1));
340	} else {
341		*stddev = 0;
342	}
343}
344
345static void print_balance_stats(const char *name, enum netdev_queue_type type,
346				__u64 *values, unsigned int count)
347{
348	double mean, stddev, cv, ns;
349	__u64 min, max;
350
351	if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
352	    (name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
353		return;
354
355	compute_stats(values, count, &mean, &stddev, &min, &max);
356
357	cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
358	ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
359
360	printf("  %-12s: cv=%.1f%% ns=%.1f%% stddev=%.0f\n",
361	       name, cv, ns, stddev);
362	printf("  %-12s  min=%llu max=%llu mean=%.0f\n",
363	       "", min, max, mean);
364}
365
366static void
367print_balance_stats_json(const char *name, enum netdev_queue_type type,
368			 __u64 *values, unsigned int count)
369{
370	double mean, stddev, cv, ns;
371	__u64 min, max;
372
373	if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
374	    (name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
375		return;
376
377	compute_stats(values, count, &mean, &stddev, &min, &max);
378
379	cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
380	ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
381
382	jsonw_name(json_wtr, name);
383	jsonw_start_object(json_wtr);
384	jsonw_uint_field(json_wtr, "queue-count", count);
385	jsonw_uint_field(json_wtr, "min", min);
386	jsonw_uint_field(json_wtr, "max", max);
387	jsonw_float_field(json_wtr, "mean", mean);
388	jsonw_float_field(json_wtr, "stddev", stddev);
389	jsonw_float_field(json_wtr, "coefficient-of-variation", cv);
390	jsonw_float_field(json_wtr, "normalized-spread", ns);
391	jsonw_end_object(json_wtr);
392}
393
394static int cmp_ifindex_type(const void *a, const void *b)
395{
396	const struct netdev_qstats_get_rsp *qa = a;
397	const struct netdev_qstats_get_rsp *qb = b;
398
399	if (qa->ifindex != qb->ifindex)
400		return qa->ifindex - qb->ifindex;
401	if (qa->queue_type != qb->queue_type)
402		return qa->queue_type - qb->queue_type;
403	return qa->queue_id - qb->queue_id;
404}
405
406static int do_balance(int argc, char **argv __attribute__((unused)))
407{
408	struct netdev_qstats_get_list *qstats;
409	struct netdev_qstats_get_req *req;
410	struct netdev_qstats_get_rsp **sorted;
411	struct ynl_error yerr;
412	struct ynl_sock *ys;
413	unsigned int count = 0;
414	unsigned int i, j;
415	int ret = 0;
416
417	if (argc > 0) {
418		p_err("balance command takes no arguments");
419		return -1;
420	}
421
422	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
423	if (!ys) {
424		p_err("YNL: %s", yerr.msg);
425		return -1;
426	}
427
428	req = netdev_qstats_get_req_alloc();
429	if (!req) {
430		p_err("failed to allocate qstats request");
431		ret = -1;
432		goto exit_close;
433	}
434
435	/* Always use queue scope for balance analysis */
436	netdev_qstats_get_req_set_scope(req, NETDEV_QSTATS_SCOPE_QUEUE);
437
438	qstats = netdev_qstats_get_dump(ys, req);
439	netdev_qstats_get_req_free(req);
440	if (!qstats) {
441		p_err("failed to get queue stats: %s", ys->err.msg);
442		ret = -1;
443		goto exit_close;
444	}
445
446	/* Count and sort queues */
447	ynl_dump_foreach(qstats, qs)
448		count++;
449
450	if (count == 0) {
451		if (json_output)
452			jsonw_start_array(json_wtr);
453		else
454			printf("No queue statistics available\n");
455		goto exit_free_qstats;
456	}
457
458	sorted = calloc(count, sizeof(*sorted));
459	if (!sorted) {
460		p_err("failed to allocate sorted array");
461		ret = -1;
462		goto exit_free_qstats;
463	}
464
465	i = 0;
466	ynl_dump_foreach(qstats, qs)
467		sorted[i++] = qs;
468
469	qsort(sorted, count, sizeof(*sorted), cmp_ifindex_type);
470
471	if (json_output)
472		jsonw_start_array(json_wtr);
473
474	/* Process each device/queue-type combination */
475	i = 0;
476	while (i < count) {
477		__u64 *rx_packets, *rx_bytes, *tx_packets, *tx_bytes;
478		enum netdev_queue_type type = sorted[i]->queue_type;
479		unsigned int ifindex = sorted[i]->ifindex;
480		unsigned int queue_count = 0;
481		char ifname[IF_NAMESIZE];
482		const char *name;
483
484		/* Count queues for this device/type */
485		for (j = i; j < count && sorted[j]->ifindex == ifindex &&
486		     sorted[j]->queue_type == type; j++)
487			queue_count++;
488
489		/* Skip if no packets/bytes (inactive queues) */
490		if (!sorted[i]->_present.rx_packets &&
491		    !sorted[i]->_present.rx_bytes &&
492		    !sorted[i]->_present.tx_packets &&
493		    !sorted[i]->_present.tx_bytes)
494			goto next_ifc;
495
496		/* Allocate arrays for statistics */
497		rx_packets = calloc(queue_count, sizeof(*rx_packets));
498		rx_bytes   = calloc(queue_count, sizeof(*rx_bytes));
499		tx_packets = calloc(queue_count, sizeof(*tx_packets));
500		tx_bytes   = calloc(queue_count, sizeof(*tx_bytes));
501
502		if (!rx_packets || !rx_bytes || !tx_packets || !tx_bytes) {
503			p_err("failed to allocate statistics arrays");
504			free(rx_packets);
505			free(rx_bytes);
506			free(tx_packets);
507			free(tx_bytes);
508			ret = -1;
509			goto exit_free_sorted;
510		}
511
512		/* Collect statistics */
513		for (j = 0; j < queue_count; j++) {
514			rx_packets[j] = sorted[i + j]->_present.rx_packets ?
515					sorted[i + j]->rx_packets : 0;
516			rx_bytes[j] = sorted[i + j]->_present.rx_bytes ?
517				      sorted[i + j]->rx_bytes : 0;
518			tx_packets[j] = sorted[i + j]->_present.tx_packets ?
519					sorted[i + j]->tx_packets : 0;
520			tx_bytes[j] = sorted[i + j]->_present.tx_bytes ?
521				      sorted[i + j]->tx_bytes : 0;
522		}
523
524		name = if_indextoname(ifindex, ifname);
525
526		if (json_output) {
527			jsonw_start_object(json_wtr);
528			if (name)
529				jsonw_string_field(json_wtr, "ifname", name);
530			jsonw_uint_field(json_wtr, "ifindex", ifindex);
531			jsonw_string_field(json_wtr, "queue-type",
532					   netdev_queue_type_str(type));
533
534			print_balance_stats_json("rx-packets", type,
535						 rx_packets, queue_count);
536			print_balance_stats_json("rx-bytes", type,
537						 rx_bytes, queue_count);
538			print_balance_stats_json("tx-packets", type,
539						 tx_packets, queue_count);
540			print_balance_stats_json("tx-bytes", type,
541						 tx_bytes, queue_count);
542
543			jsonw_end_object(json_wtr);
544		} else {
545			if (name)
546				printf("%s", name);
547			else
548				printf("ifindex:%u", ifindex);
549			printf(" %s %d queues:\n",
550			       netdev_queue_type_str(type), queue_count);
551
552			print_balance_stats("rx-packets", type,
553					    rx_packets, queue_count);
554			print_balance_stats("rx-bytes", type,
555					    rx_bytes, queue_count);
556			print_balance_stats("tx-packets", type,
557					    tx_packets, queue_count);
558			print_balance_stats("tx-bytes", type,
559					    tx_bytes, queue_count);
560			printf("\n");
561		}
562
563		free(rx_packets);
564		free(rx_bytes);
565		free(tx_packets);
566		free(tx_bytes);
567
568next_ifc:
569		i += queue_count;
570	}
571
572	if (json_output)
573		jsonw_end_array(json_wtr);
574
575exit_free_sorted:
576	free(sorted);
577exit_free_qstats:
578	netdev_qstats_get_list_free(qstats);
579exit_close:
580	ynl_sock_destroy(ys);
581	return ret;
582}
583
584static int do_help(int argc __attribute__((unused)),
585		   char **argv __attribute__((unused)))
586{
587	if (json_output) {
588		jsonw_null(json_wtr);
589		return 0;
590	}
591
592	fprintf(stderr,
593		"Usage: %s qstats { COMMAND | help }\n"
594		"       %s qstats [ show ] [ OPTIONS ]\n"
595		"       %s qstats balance\n"
596		"\n"
597		"       OPTIONS := { scope queue | group-by { device | queue } }\n"
598		"\n"
599		"       show                  - Display queue statistics (default)\n"
600		"                               Statistics are aggregated for the entire device.\n"
601		"       show scope queue      - Display per-queue statistics\n"
602		"       show group-by device  - Display device-aggregated statistics (default)\n"
603		"       show group-by queue   - Display per-queue statistics\n"
604		"       balance               - Analyze traffic distribution balance.\n"
605		"",
606		bin_name, bin_name, bin_name);
607
608	return 0;
609}
610
611static const struct cmd qstats_cmds[] = {
612	{ "show",	do_show },
613	{ "balance",	do_balance },
614	{ "help",	do_help },
615	{ 0 }
616};
617
618int do_qstats(int argc, char **argv)
619{
620	return cmd_select(qstats_cmds, argc, argv, do_help);
621}
~/ Read-only file preview.