}
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;
}
}
case OPT_BLOCKING_TIMEOUT:
{
- long long v; /* in usec */
+ uint64_t v;
long long v_msec;
errno = 0;
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;
}
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
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);
test_session \
test_uri \
test_utils_parse_size_suffix \
+ test_utils_parse_time_suffix \
test_utils_expand_path \
test_string_utils \
test_notification \
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
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)
--- /dev/null
+/*
+ * 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();
+}