Add -p switch to "zpool get"
[zfs.git] / cmd / zpios / zpios_util.c
1 /*****************************************************************************\
2  *  ZPIOS is a heavily modified version of the original PIOS test code.
3  *  It is designed to have the test code running in the Linux kernel
4  *  against ZFS while still being flexibly controled from user space.
5  *
6  *  Copyright (C) 2008-2010 Lawrence Livermore National Security, LLC.
7  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
8  *  Written by Brian Behlendorf <behlendorf1@llnl.gov>.
9  *  LLNL-CODE-403049
10  *
11  *  Original PIOS Test Code
12  *  Copyright (C) 2004 Cluster File Systems, Inc.
13  *  Written by Peter Braam <braam@clusterfs.com>
14  *             Atul Vidwansa <atul@clusterfs.com>
15  *             Milind Dumbare <milind@clusterfs.com>
16  *
17  *  This file is part of ZFS on Linux.
18  *  For details, see <http://zfsonlinux.org/>.
19  *
20  *  ZPIOS is free software; you can redistribute it and/or modify it
21  *  under the terms of the GNU General Public License as published by the
22  *  Free Software Foundation; either version 2 of the License, or (at your
23  *  option) any later version.
24  *
25  *  ZPIOS is distributed in the hope that it will be useful, but WITHOUT
26  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
27  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
28  *  for more details.
29  *
30  *  You should have received a copy of the GNU General Public License along
31  *  with ZPIOS.  If not, see <http://www.gnu.org/licenses/>.
32 \*****************************************************************************/
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <regex.h>
40 #include "zpios.h"
41
42 /* extracts an unsigned int (64) and K,M,G,T from the string */
43 /* and returns a 64 bit value converted to the proper units */
44 static int
45 kmgt_to_uint64(const char *str, uint64_t *val)
46 {
47         char *endptr;
48         int rc = 0;
49
50         *val = strtoll(str, &endptr, 0);
51         if ((str == endptr) && (*val == 0))
52                 return EINVAL;
53
54         switch (endptr[0]) {
55                 case 'k': case 'K':
56                         *val = (*val) << 10;
57                         break;
58                 case 'm': case 'M':
59                         *val = (*val) << 20;
60                         break;
61                 case 'g': case 'G':
62                         *val = (*val) << 30;
63                         break;
64                 case 't': case 'T':
65                         *val = (*val) << 40;
66                         break;
67                 case '\0':
68                         break;
69                 default:
70                         rc = EINVAL;
71         }
72
73         return rc;
74 }
75
76 static char *
77 uint64_to_kmgt(char *str, uint64_t val)
78 {
79         char postfix[] = "kmgt";
80         int i = -1;
81
82         while ((val >= KB) && (i < 4)) {
83                 val = (val >> 10);
84                 i++;
85         }
86
87         if (i >= 4)
88                 (void)snprintf(str, KMGT_SIZE-1, "inf");
89         else
90                 (void)snprintf(str, KMGT_SIZE-1, "%lu%c", (unsigned long)val,
91                                (i == -1) ? '\0' : postfix[i]);
92
93         return str;
94 }
95
96 static char *
97 kmgt_per_sec(char *str, uint64_t v, double t)
98 {
99         char postfix[] = "kmgt";
100         double val = ((double)v) / t;
101         int i = -1;
102
103         while ((val >= (double)KB) && (i < 4)) {
104                 val /= (double)KB;
105                 i++;
106         }
107
108         if (i >= 4)
109                 (void)snprintf(str, KMGT_SIZE-1, "inf");
110         else
111                 (void)snprintf(str, KMGT_SIZE-1, "%.2f%c", val,
112                                (i == -1) ? '\0' : postfix[i]);
113
114         return str;
115 }
116
117 static char *
118 print_flags(char *str, uint32_t flags)
119 {
120         str[0] = (flags & DMU_WRITE)  ? 'w' : '-';
121         str[1] = (flags & DMU_READ)   ? 'r' : '-';
122         str[2] = (flags & DMU_VERIFY) ? 'v' : '-';
123         str[3] = (flags & DMU_REMOVE) ? 'c' : '-';
124         str[4] = (flags & DMU_FPP)    ? 'p' : 's';
125         str[5] = (flags & (DMU_WRITE_ZC | DMU_READ_ZC)) ? 'z' : '-';
126         str[6] = (flags & DMU_WRITE_NOWAIT) ? 'O' : '-';
127         str[7] = '\0';
128
129         return str;
130 }
131
132 static int
133 regex_match(const char *string, char *pattern)
134 {
135         regex_t re = { 0 };
136         int rc;
137
138         rc = regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB | REG_ICASE);
139         if (rc) {
140                 fprintf(stderr, "Error: Couldn't do regcomp, %d\n", rc);
141                 return rc;
142         }
143
144         rc = regexec(&re, string, (size_t) 0, NULL, 0);
145         regfree(&re);
146
147         return rc;
148 }
149
150 /* fills the pios_range_repeat structure of comma separated values */
151 static int
152 split_string(const char *optarg, char *pattern, range_repeat_t *range)
153 {
154         const char comma[] = ",";
155         char *cp, *token[32];
156         int rc, i = 0;
157
158         if ((rc = regex_match(optarg, pattern)))
159                 return rc;
160
161         cp = strdup(optarg);
162         if (cp == NULL)
163                 return ENOMEM;
164
165         do {
166                 /* STRTOK(3) Each subsequent call, with a null pointer as the
167                  * value of the * first  argument, starts searching from the
168                  * saved pointer and behaves as described above.
169                  */
170                 token[i] = strtok(cp, comma);
171                 cp = NULL;
172         } while ((token[i++] != NULL) && (i < 32));
173
174         range->val_count = i - 1;
175
176         for (i = 0; i < range->val_count; i++)
177                 kmgt_to_uint64(token[i], &range->val[i]);
178
179         free(cp);
180         return 0;
181 }
182
183 int
184 set_count(char *pattern1, char *pattern2, range_repeat_t *range,
185           char *optarg, uint32_t *flags, char *arg)
186 {
187         if (flags)
188                 *flags |= FLAG_SET;
189
190         range->next_val = 0;
191
192         if (regex_match(optarg, pattern1) == 0) {
193                 kmgt_to_uint64(optarg, &range->val[0]);
194                 range->val_count = 1;
195         } else if (split_string(optarg, pattern2, range) < 0) {
196                 fprintf(stderr, "Error: Incorrect pattern for %s, '%s'\n",
197                         arg, optarg);
198                 return EINVAL;
199         }
200
201         return 0;
202 }
203
204 /* validates the value with regular expression and sets low, high, incr
205  * according to value at which flag will be set. Sets the flag after. */
206 int
207 set_lhi(char *pattern, range_repeat_t *range, char *optarg,
208         int flag, uint32_t *flag_thread, char *arg)
209 {
210         int rc;
211
212         if ((rc = regex_match(optarg, pattern))) {
213                 fprintf(stderr, "Error: Wrong pattern in %s, '%s'\n",
214                         arg, optarg);
215                 return rc;
216         }
217
218         switch (flag) {
219                 case FLAG_LOW:
220                         kmgt_to_uint64(optarg, &range->val_low);
221                         break;
222                 case FLAG_HIGH:
223                         kmgt_to_uint64(optarg, &range->val_high);
224                         break;
225                 case FLAG_INCR:
226                         kmgt_to_uint64(optarg, &range->val_inc_perc);
227                         break;
228                 default:
229                         assert(0);
230         }
231
232         *flag_thread |= flag;
233
234         return 0;
235 }
236
237 int
238 set_noise(uint64_t *noise, char *optarg, char *arg)
239 {
240         if (regex_match(optarg, REGEX_NUMBERS) == 0) {
241                 kmgt_to_uint64(optarg, noise);
242         } else {
243                 fprintf(stderr, "Error: Incorrect pattern for %s\n", arg);
244                 return EINVAL;
245         }
246
247         return 0;
248 }
249
250 int
251 set_load_params(cmd_args_t *args, char *optarg)
252 {
253         char *param, *search, comma[] = ",";
254         int rc = 0;
255
256         search = strdup(optarg);
257         if (search == NULL)
258                 return ENOMEM;
259
260         while ((param = strtok(search, comma)) != NULL) {
261                 search = NULL;
262
263                 if (strcmp("fpp", param) == 0) {
264                         args->flags |= DMU_FPP; /* File Per Process/Thread */
265                 } else if (strcmp("ssf", param) == 0) {
266                         args->flags &= ~DMU_FPP; /* Single Shared File */
267                 } else if (strcmp("dmuio", param) == 0) {
268                         args->io_type |= DMU_IO;
269                         args->flags |= (DMU_WRITE | DMU_READ);
270                 } else {
271                         fprintf(stderr, "Invalid load: %s\n", param);
272                         rc = EINVAL;
273                 }
274         }
275
276         free(search);
277
278         return rc;
279 }
280
281
282 /* checks the low, high, increment values against the single value for
283  * mutual exclusion, for e.g threadcount is mutually exclusive to
284  * threadcount_low, ..._high, ..._incr */
285 int
286 check_mutual_exclusive_command_lines(uint32_t flag, char *arg)
287 {
288         if ((flag & FLAG_SET) && (flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR))) {
289                 fprintf(stderr, "Error: --%s can not be given with --%s_low, "
290                         "--%s_high or --%s_incr.\n", arg, arg, arg, arg);
291                 return 0;
292         }
293
294         if ((flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) && !(flag & FLAG_SET)){
295                 if (flag != (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) {
296                         fprintf(stderr, "Error: One or more values missing "
297                                 "from --%s_low, --%s_high, --%s_incr.\n",
298                                 arg, arg, arg);
299                         return 0;
300                 }
301         }
302
303         return 1;
304 }
305
306 void
307 print_stats_header(cmd_args_t *args)
308 {
309         if (args->verbose) {
310                 printf("status    name        id\tth-cnt\trg-cnt\trg-sz\t"
311                        "ch-sz\toffset\trg-no\tch-no\tth-dly\tflags\ttime\t"
312                        "cr-time\trm-time\twr-time\trd-time\twr-data\twr-ch\t"
313                        "wr-bw\trd-data\trd-ch\trd-bw\n");
314                 printf("------------------------------------------------"
315                        "------------------------------------------------"
316                        "------------------------------------------------"
317                        "----------------------------------------------\n");
318         } else {
319                 printf("status    name        id\t"
320                        "wr-data\twr-ch\twr-bw\t"
321                        "rd-data\trd-ch\trd-bw\n");
322                 printf("-----------------------------------------"
323                        "--------------------------------------\n");
324         }
325 }
326
327 static void
328 print_stats_human_readable(cmd_args_t *args, zpios_cmd_t *cmd)
329 {
330         zpios_stats_t *summary_stats;
331         double t_time, wr_time, rd_time, cr_time, rm_time;
332         char str[KMGT_SIZE];
333
334         if (args->rc)
335                 printf("FAIL: %3d ", args->rc);
336         else
337                 printf("PASS:     ");
338
339         printf("%-12s", args->name ? args->name : ZPIOS_NAME);
340         printf("%2u\t", cmd->cmd_id);
341
342         if (args->verbose) {
343                 printf("%u\t", cmd->cmd_thread_count);
344                 printf("%u\t", cmd->cmd_region_count);
345                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_size));
346                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_size));
347                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_offset));
348                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_noise));
349                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_noise));
350                 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_thread_delay));
351                 printf("%s\t", print_flags(str, cmd->cmd_flags));
352         }
353
354         if (args->rc) {
355                 printf("\n");
356                 return;
357         }
358
359         summary_stats = (zpios_stats_t *)cmd->cmd_data_str;
360         t_time  = zpios_timespec_to_double(summary_stats->total_time.delta);
361         wr_time = zpios_timespec_to_double(summary_stats->wr_time.delta);
362         rd_time = zpios_timespec_to_double(summary_stats->rd_time.delta);
363         cr_time = zpios_timespec_to_double(summary_stats->cr_time.delta);
364         rm_time = zpios_timespec_to_double(summary_stats->rm_time.delta);
365
366         if (args->verbose) {
367                 printf("%.2f\t", t_time);
368                 printf("%.3f\t", cr_time);
369                 printf("%.3f\t", rm_time);
370                 printf("%.2f\t", wr_time);
371                 printf("%.2f\t", rd_time);
372         }
373
374         printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_data));
375         printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_chunks));
376         printf("%s\t", kmgt_per_sec(str, summary_stats->wr_data, wr_time));
377
378         printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_data));
379         printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_chunks));
380         printf("%s\n", kmgt_per_sec(str, summary_stats->rd_data, rd_time));
381         fflush(stdout);
382 }
383
384 static void
385 print_stats_table(cmd_args_t *args, zpios_cmd_t *cmd)
386 {
387         zpios_stats_t *summary_stats;
388         double wr_time, rd_time;
389
390         if (args->rc)
391                 printf("FAIL: %3d ", args->rc);
392         else
393                 printf("PASS:     ");
394
395         printf("%-12s", args->name ? args->name : ZPIOS_NAME);
396         printf("%2u\t", cmd->cmd_id);
397
398         if (args->verbose) {
399                 printf("%u\t", cmd->cmd_thread_count);
400                 printf("%u\t", cmd->cmd_region_count);
401                 printf("%llu\t", (long long unsigned)cmd->cmd_region_size);
402                 printf("%llu\t", (long long unsigned)cmd->cmd_chunk_size);
403                 printf("%llu\t", (long long unsigned)cmd->cmd_offset);
404                 printf("%u\t", cmd->cmd_region_noise);
405                 printf("%u\t", cmd->cmd_chunk_noise);
406                 printf("%u\t", cmd->cmd_thread_delay);
407                 printf("0x%x\t", cmd->cmd_flags);
408         }
409
410         if (args->rc) {
411                 printf("\n");
412                 return;
413         }
414
415         summary_stats = (zpios_stats_t *)cmd->cmd_data_str;
416         wr_time = zpios_timespec_to_double(summary_stats->wr_time.delta);
417         rd_time = zpios_timespec_to_double(summary_stats->rd_time.delta);
418
419         if (args->verbose) {
420                 printf("%ld.%02ld\t",
421                        (long)summary_stats->total_time.delta.ts_sec,
422                        (long)summary_stats->total_time.delta.ts_nsec);
423                 printf("%ld.%02ld\t",
424                        (long)summary_stats->cr_time.delta.ts_sec,
425                        (long)summary_stats->cr_time.delta.ts_nsec);
426                 printf("%ld.%02ld\t",
427                        (long)summary_stats->rm_time.delta.ts_sec,
428                        (long)summary_stats->rm_time.delta.ts_nsec);
429                 printf("%ld.%02ld\t",
430                        (long)summary_stats->wr_time.delta.ts_sec,
431                        (long)summary_stats->wr_time.delta.ts_nsec);
432                 printf("%ld.%02ld\t",
433                        (long)summary_stats->rd_time.delta.ts_sec,
434                        (long)summary_stats->rd_time.delta.ts_nsec);
435         }
436
437         printf("%lld\t", (long long unsigned)summary_stats->wr_data);
438         printf("%lld\t", (long long unsigned)summary_stats->wr_chunks);
439         printf("%.4f\t", (double)summary_stats->wr_data / wr_time);
440
441         printf("%lld\t", (long long unsigned)summary_stats->rd_data);
442         printf("%lld\t", (long long unsigned)summary_stats->rd_chunks);
443         printf("%.4f\n", (double)summary_stats->rd_data / rd_time);
444         fflush(stdout);
445 }
446
447 void
448 print_stats(cmd_args_t *args, zpios_cmd_t *cmd)
449 {
450         if (args->human_readable)
451                 print_stats_human_readable(args, cmd);
452         else
453                 print_stats_table(args, cmd);
454 }