Introduce utils_parse_time_suffix
authorSimon Marchi <simon.marchi@polymtl.ca>
Wed, 17 Jun 2015 18:07:58 +0000 (14:07 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 28 Mar 2018 02:10:43 +0000 (22:10 -0400)
This function is based on utils_parse_size_suffix, but is to parse
(relatively short) time suffixes. It returns the time in microseconds.
So far, it supports:

 - u/us: microseconds, x1, same as no suffix
 - m/ms: milliseconds, x1 000
 - s: seconds, x1 000 000

 For example:

 - 32u becomes 32
 - 32us becomes 32
 - 32m becomes 32 000
 - 32ms becomes 32 000
 - 32s becomes 32 000 000

Signed-off-by: Simon Marchi <simon.marchi@polymtl.ca>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng/commands/enable_channels.c
src/common/utils.c
src/common/utils.h
tests/unit/Makefile.am
tests/unit/test_utils_parse_time_suffix.c [new file with mode: 0644]

index 9c84d02634c8612740b914c4a1c6d0d5e806b210..214c8a3621660c8cf83cbd31edf26e7b7c06aebc 100644 (file)
@@ -535,13 +535,13 @@ int cmd_enable_channels(int argc, const char **argv)
                }
                case OPT_MONITOR_TIMER:
                {
-                       unsigned long long v;
+                       uint64_t v;
 
                        errno = 0;
                        opt_arg = poptGetOptArg(pc);
-                       v = strtoull(opt_arg, NULL, 0);
-                       if (errno != 0 || !isdigit(opt_arg[0])) {
-                               ERR("Wrong value in --monitor-timer parameter: %s", opt_arg);
+
+                       if (utils_parse_time_suffix(opt_arg, &v) < 0) {
+                               ERR("Wrong value for --monitor-timer parameter: %s", opt_arg);
                                ret = CMD_ERROR;
                                goto end;
                        }
@@ -552,7 +552,7 @@ int cmd_enable_channels(int argc, const char **argv)
                }
                case OPT_BLOCKING_TIMEOUT:
                {
-                       long long v;    /* in usec */
+                       uint64_t v;
                        long long v_msec;
 
                        errno = 0;
@@ -565,10 +565,8 @@ int cmd_enable_channels(int argc, const char **argv)
                                break;
                        }
 
-                       v = strtoll(opt_arg, NULL, 0);
-                       if (errno != 0 || (!isdigit(opt_arg[0]) && opt_arg[0] != '-')
-                                       || v < 0) {
-                               ERR("Wrong value in --blocking-timeout parameter: %s", opt_arg);
+                       if (utils_parse_time_suffix(opt_arg, &v) < 0) {
+                               ERR("Wrong value for --blocking-timeout parameter: %s", opt_arg);
                                ret = CMD_ERROR;
                                goto end;
                        }
index f1e0148cb9cb9cd28159846141a99fc108b95a58..004cd8f0d1dcee02aa4b22a4e7f5c6f84a4618f7 100644 (file)
@@ -1001,6 +1001,107 @@ end:
        return ret;
 }
 
+/**
+ * Parse a string that represents a time in human readable format. It
+ * supports decimal integers suffixed by 's', 'u', 'm', 'us', and 'ms'.
+ *
+ * The suffix multiply the integer by:
+ * 'u'/'us': 1
+ * 'm'/'ms': 1000
+ * 's': 1000000
+ *
+ * Note that unit-less numbers are assumed to be microseconds.
+ *
+ * @param str          The string to parse, assumed to be NULL-terminated.
+ * @param time_us      Pointer to a uint64_t that will be filled with the
+ *                     resulting time in microseconds.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+LTTNG_HIDDEN
+int utils_parse_time_suffix(char const * const str, uint64_t * const time_us)
+{
+       int ret;
+       uint64_t base_time;
+       long multiplier = 1;
+       const char *str_end;
+       char *num_end;
+
+       if (!str) {
+               DBG("utils_parse_time_suffix: received a NULL string.");
+               ret = -1;
+               goto end;
+       }
+
+       /* strtoull will accept a negative number, but we don't want to. */
+       if (strchr(str, '-') != NULL) {
+               DBG("utils_parse_time_suffix: invalid time string, should not contain '-'.");
+               ret = -1;
+               goto end;
+       }
+
+       /* str_end will point to the \0 */
+       str_end = str + strlen(str);
+       errno = 0;
+       base_time = strtoull(str, &num_end, 10);
+       if (errno != 0) {
+               PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str);
+               ret = -1;
+               goto end;
+       }
+
+       if (num_end == str) {
+               /* strtoull parsed nothing, not good. */
+               DBG("utils_parse_time_suffix: strtoull had nothing good to parse.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check if a prefix is present. */
+       switch (*num_end) {
+       case 'u':
+               multiplier = 1;
+               /* Skip another letter in the 'us' case. */
+               num_end += (*(num_end + 1) == 's') ? 2 : 1;
+               break;
+       case 'm':
+               multiplier = 1000;
+               /* Skip another letter in the 'ms' case. */
+               num_end += (*(num_end + 1) == 's') ? 2 : 1;
+               break;
+       case 's':
+               multiplier = 1000000;
+               num_end++;
+               break;
+       case '\0':
+               break;
+       default:
+               DBG("utils_parse_time_suffix: invalid suffix.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check for garbage after the valid input. */
+       if (num_end != str_end) {
+               DBG("utils_parse_time_suffix: Garbage after time string.");
+               ret = -1;
+               goto end;
+       }
+
+       *time_us = base_time * multiplier;
+
+       /* Check for overflow */
+       if ((*time_us / multiplier) != base_time) {
+               DBG("utils_parse_time_suffix: oops, overflow detected.");
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
 /*
  * fls: returns the position of the most significant bit.
  * Returns 0 if no bit is set, else returns the position of the most
index 18f19ef1abd078bc19ac9f5aca21100e67d9e997..9293b1d0ac03d44bf6bc052cb95af1a8fdbf7641 100644 (file)
@@ -47,6 +47,7 @@ int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
                uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
                int *stream_fd);
 int utils_parse_size_suffix(char const * const str, uint64_t * const size);
+int utils_parse_time_suffix(char const * const str, uint64_t * const time_us);
 int utils_get_count_order_u32(uint32_t x);
 int utils_get_count_order_u64(uint64_t x);
 char *utils_get_home_dir(void);
index 340dd936570929775686f6fc7e3b3064ac16138c..1ff6ab1d03aaaf7dd97999322de55fa4e4ff3a77 100644 (file)
@@ -10,6 +10,7 @@ TESTS = test_kernel_data \
        test_session \
        test_uri \
        test_utils_parse_size_suffix \
+       test_utils_parse_time_suffix \
        test_utils_expand_path \
        test_string_utils \
        test_notification \
@@ -25,9 +26,9 @@ LIBRELAYD=$(top_builddir)/src/common/relayd/librelayd.la
 LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
 
 # Define test programs
-noinst_PROGRAMS = test_uri test_session test_kernel_data
-noinst_PROGRAMS += test_utils_parse_size_suffix test_utils_expand_path
-noinst_PROGRAMS += test_string_utils test_notification
+noinst_PROGRAMS = test_uri test_session test_kernel_data \
+                  test_utils_parse_size_suffix test_utils_parse_time_suffix \
+                  test_utils_expand_path test_string_utils test_notification
 
 if HAVE_LIBLTTNG_UST_CTL
 noinst_PROGRAMS += test_ust_data
@@ -103,6 +104,11 @@ test_utils_parse_size_suffix_SOURCES = test_utils_parse_size_suffix.c
 test_utils_parse_size_suffix_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS)
 test_utils_parse_size_suffix_LDADD += $(UTILS_SUFFIX)
 
+# parse_time_suffix unit test
+test_utils_parse_time_suffix_SOURCES = test_utils_parse_time_suffix.c
+test_utils_parse_time_suffix_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON)
+test_utils_parse_time_suffix_LDADD += $(UTILS_SUFFIX)
+
 # expand_path unit test
 test_utils_expand_path_SOURCES = test_utils_expand_path.c
 test_utils_expand_path_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS)
diff --git a/tests/unit/test_utils_parse_time_suffix.c b/tests/unit/test_utils_parse_time_suffix.c
new file mode 100644 (file)
index 0000000..9648276
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) - 2015 Simon Marchi <simon.marchi@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by as
+ * published by the Free Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <tap/tap.h>
+
+#include <common/utils.h>
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose = 3;
+int lttng_opt_mi;
+
+struct valid_test_input {
+       char *input;
+       uint64_t expected_result;
+};
+
+/* Valid test cases */
+static struct valid_test_input valid_tests_inputs[] = {
+               { "0", 0 },
+               { "1234", 1234 },
+               { "0u", 0 },
+               { "1234u", 1234 },
+               { "16m", 16000 },
+               { "128m", 128000 },
+               { "32s", 32000000 },
+               { "00", 0 },
+               { "0m", 0 },
+               { "0s", 0 },
+               { "00m", 0 },
+               { "00s", 0 },
+               { "12ms", 12000 },
+               { "3597us", 3597 },
+               { "+5", 5 },
+               { "08", 8 },
+               { "0145us", 145 },
+};
+static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]);
+
+/* Invalid test cases */
+static char *invalid_tests_inputs[] = {
+               "",
+               " ",
+               "-1",
+               "m",
+               "4611686018427387904s",
+               "0x40M",
+               "0x",
+               "x0",
+               "0xx0",
+               "07mm",
+               "0xm",
+               "0Xs",
+               "0x0ss",
+               "0a",
+               "0B",
+               "0x3 s",
+               "0xbs ",
+               "14ns",
+               "0xbs",
+               "14ns",
+               "14ms garbage after value",
+               "0x14s",
+};
+static const int num_invalid_tests = sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]);
+
+static void test_utils_parse_time_suffix(void)
+{
+       uint64_t result;
+       int ret;
+       int i;
+
+       /* Test valid cases */
+       for (i = 0; i < num_valid_tests; i++) {
+               char name[100];
+
+               sprintf(name, "valid test case: %s", valid_tests_inputs[i].input);
+
+               ret = utils_parse_time_suffix(valid_tests_inputs[i].input, &result);
+               ok(ret == 0 && result == valid_tests_inputs[i].expected_result, name);
+       }
+
+       /* Test invalid cases */
+       for (i = 0; i < num_invalid_tests; i++) {
+               char name[100];
+
+               sprintf(name, "invalid test case: %s", invalid_tests_inputs[i]);
+
+               ret = utils_parse_time_suffix(invalid_tests_inputs[i], &result);
+               ok(ret != 0, name);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       plan_tests(num_valid_tests + num_invalid_tests);
+
+       diag("utils_parse_time_suffix tests");
+
+       test_utils_parse_time_suffix();
+
+       return exit_status();
+}
This page took 0.041829 seconds and 4 git commands to generate.