This is just a re-work of Steve's sample patch.
You set the number of splits and whether they overwrite using
qb_util_stopwatch_split_ctl().
Signed-off-by: Angus Salkeld <asalkeld(a)redhat.com>
---
include/qb/qbutil.h | 117 +++++++++++++++++++++++++++++++
lib/util.c | 88 ++++++++++++++++++++++++
tests/Makefile.am | 8 ++-
tests/check_util.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 401 insertions(+), 2 deletions(-)
create mode 100644 tests/check_util.c
diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h
index 2b450e9..4392b63 100644
--- a/include/qb/qbutil.h
+++ b/include/qb/qbutil.h
@@ -38,6 +38,72 @@ extern "C" {
* @author Angus Salkeld <asalkeld(a)redhat.com>
*
* These are some convience functions used throughout libqb.
+ *
+ * @par Locking
+ * - qb_thread_lock_create()
+ * - qb_thread_lock()
+ * - qb_thread_trylock()
+ * - qb_thread_unlock()
+ * - qb_thread_lock_destroy()
+ *
+ * @par Time functions
+ * - qb_timespec_add_ms()
+ * - qb_util_nano_current_get()
+ * - qb_util_nano_monotonic_hz()
+ * - qb_util_nano_from_epoch_get()
+ * - qb_util_timespec_from_epoch_get()
+ *
+ * @par Basic Stopwatch
+ * @code
+ * uint64_t elapsed1;
+ * uint64_t elapsed2;
+ * qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+ *
+ * qb_util_stopwatch_start(sw);
+ *
+ * usleep(sometime);
+ * qb_util_stopwatch_stop(sw);
+ * elapsed1 = qb_util_stopwatch_us_elapsed_get(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_stop(sw);
+ * elapsed2 = qb_util_stopwatch_us_elapsed_get(sw);
+ *
+ * qb_util_stopwatch_free(sw);
+ * @endcode
+ *
+ * @par Stopwatch with splits
+ * Setup a stopwatch with space for 3 splits.
+ *
+ * @code
+ * uint64_t split;
+ * qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+ *
+ * qb_util_stopwatch_split_ctl(sw, 3, 0);
+ * qb_util_stopwatch_start(sw);
+ *
+ * usleep(sometime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * idx = qb_util_stopwatch_split_last(sw);
+ * do {
+ * split = qb_util_stopwatch_time_split_get(sw, idx, idx);
+ * qb_log(LOG_INFO, "split %d is %"PRIu64"", last, split);
+ * idx--;
+ * } while (split > 0);
+ *
+ * split = qb_util_stopwatch_time_split_get(sw, 2, 1);
+ * qb_log(LOG_INFO, "time between second and third split is
%"PRIu64"", split);
+ *
+ * qb_util_stopwatch_free(sw);
+ * @endcode
+ *
*/
/**
@@ -130,6 +196,9 @@ char *qb_strerror_r(int errnum, char *buf, size_t buflen);
typedef struct qb_util_stopwatch qb_util_stopwatch_t;
+#define QB_UTIL_SW_OVERWRITE 0x01
+
+
/**
* Create a Stopwatch (to time operations)
*/
@@ -171,6 +240,54 @@ uint64_t qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t *sw);
*/
float qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *sw);
+/**
+ *
+ * @param sw the stopwatch
+ * @param max_splits maximum number of time splits
+ * @param options (0 or QB_UTIL_SW_OVERWRITE )
+ * @retval 0 on success
+ * @retval -errno on failure
+ */
+int32_t qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
+ uint32_t max_splits, uint32_t options);
+
+/**
+ * Create a new time split (or lap time)
+ *
+ * @param sw the stopwatch
+ * @retval the relative split time in micro seconds
+ * @retval 0 if no more splits available
+ */
+uint64_t qb_util_stopwatch_split(qb_util_stopwatch_t *sw);
+
+/**
+ * Get the last split index to be used by
+ * qb_util_stopwatch_time_split_get()
+ *
+ * @note this is zero based
+ *
+ * @param sw the stopwatch
+ * @return the last entry index
+ */
+uint32_t
+qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw);
+
+/**
+ * Read the time split from "receint" to "older".
+ *
+ * If older == receint then the cumulated split will be
+ * returned (from the stopwatch start).
+ *
+ * @param sw the stopwatch
+ * @param receint split
+ * @param older split
+ * @retval the split time in micro seconds
+ * @retval 0 if not a valid split
+ */
+uint64_t
+qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
+ uint32_t receint, uint32_t older);
+
/* *INDENT-OFF* */
#ifdef __cplusplus
}
diff --git a/lib/util.c b/lib/util.c
index 51c94bb..c13d547 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -223,6 +223,10 @@ qb_util_nano_from_epoch_get(void)
struct qb_util_stopwatch {
uint64_t started;
uint64_t stopped;
+ uint32_t split_options;
+ uint32_t split_size;
+ uint32_t split_entries;
+ uint64_t *split_entry_list;
};
qb_util_stopwatch_t *
@@ -236,6 +240,7 @@ qb_util_stopwatch_create(void)
void
qb_util_stopwatch_free(qb_util_stopwatch_t * sw)
{
+ free(sw->split_entry_list);
free(sw);
}
@@ -272,3 +277,86 @@ qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
return ((float)e6 / (float)QB_TIME_US_IN_SEC);
}
+int32_t
+qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
+ uint32_t max_splits, uint32_t options)
+{
+ sw->split_size = max_splits;
+ sw->split_options = options;
+ sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits);
+ if (sw->split_entry_list == NULL) {
+ return -errno;
+ }
+ return 0;
+}
+
+uint64_t
+qb_util_stopwatch_split(qb_util_stopwatch_t *sw)
+{
+ uint32_t new_entry_pos;
+ uint64_t time_start;
+ uint64_t time_end;
+
+ if (sw->split_size == 0) {
+ return 0;
+ }
+ if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) &&
+ sw->split_entries == sw->split_size) {
+ return 0;
+ }
+ if (sw->started == 0) {
+ qb_util_stopwatch_start(sw);
+ }
+ new_entry_pos = sw->split_entries % (sw->split_size);
+ sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get();
+ sw->split_entries++;
+
+ time_start = sw->split_entry_list[new_entry_pos];
+ if (sw->split_entries == 1) {
+ /* first entry */
+ time_end = sw->started;
+ } else if (new_entry_pos == 0) {
+ /* wrap around */
+ time_end = sw->split_entry_list[sw->split_size - 1];
+ } else {
+ time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size];
+ }
+ return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
+
+uint32_t
+qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw)
+{
+ if (sw->split_entries) {
+ return sw->split_entries - 1;
+ }
+ return sw->split_entries;
+}
+
+uint64_t
+qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
+ uint32_t receint, uint32_t older)
+{
+ uint64_t time_start;
+ uint64_t time_end;
+
+ if (sw->started == 0 ||
+ receint >= sw->split_entries ||
+ older >= sw->split_entries ||
+ receint < older) {
+ return 0;
+ }
+ if (sw->split_options & QB_UTIL_SW_OVERWRITE &&
+ (receint < (sw->split_entries - sw->split_size) ||
+ older < (sw->split_entries - sw->split_size))) {
+ return 0;
+ }
+
+ time_start = sw->split_entry_list[receint % (sw->split_size)];
+ if (older == receint) {
+ time_end = sw->started;
+ } else {
+ time_end = sw->split_entry_list[older % (sw->split_size)];
+ }
+ return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
\ No newline at end of file
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d8f64e2..4ced485 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -73,11 +73,11 @@ bench_log_LDADD = $(LIB_RT) $(top_builddir)/lib/libqb.la
if HAVE_CHECK
EXTRA_DIST += resources.test
-TESTS = array.test map.test rb.test log.test loop.test ipc.test resources.test
+TESTS = array.test map.test util.test rb.test log.test loop.test ipc.test resources.test
resources.log: rb.log log.log ipc.log
-check_PROGRAMS = array.test map.test rb.test log.test loop.test ipc.test
+check_PROGRAMS = array.test map.test rb.test log.test loop.test ipc.test util.test
check_SCRIPTS = resources.test
array_test_SOURCES = check_array.c $(top_builddir)/include/qb/qbarray.h
@@ -104,6 +104,10 @@ log_test_SOURCES = check_log.c $(top_builddir)/include/qb/qblog.h
log_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
log_test_LDADD = $(top_builddir)/lib/libqb.la $(LIB_RT) @CHECK_LIBS@
+util_test_SOURCES = check_util.c $(top_builddir)/include/qb/qbutil.h
+util_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+util_test_LDADD = $(top_builddir)/lib/libqb.la $(LIB_RT) @CHECK_LIBS@
+
endif
clean-generic:
diff --git a/tests/check_util.c b/tests/check_util.c
new file mode 100644
index 0000000..a948739
--- /dev/null
+++ b/tests/check_util.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake <sdake(a)redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb. If not, see <
http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+#define assert_int_between(_c, _lower, _upper) \
+_ck_assert_int(_c, >=, _lower); \
+_ck_assert_int(_c, <=, _upper);
+
+
+START_TEST(test_check_overwrite)
+{
+ uint64_t res;
+ uint32_t last;
+ qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+
+ qb_util_stopwatch_split_ctl(sw, 5, QB_UTIL_SW_OVERWRITE);
+
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 0, 100);
+
+ usleep(10000);
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 9000, 11000);
+
+ usleep(20000);
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 19000, 21000);
+
+ usleep(30000);
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 29000, 31000);
+
+ usleep(40000);
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 39000, 41000);
+
+ /*
+ * window should be 100000 (40000 + 30000 + 20000 + 10000) usec
+ */
+ last = qb_util_stopwatch_split_last(sw);
+ res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
+ assert_int_between(res, 95000, 105000);
+
+ usleep(50000);
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 49000, 52000);
+ /*
+ * window should be 140000 (50000 + 40000 + 30000 + 20000) usec
+ */
+ last = qb_util_stopwatch_split_last(sw);
+ res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
+ assert_int_between(res, 135000, 145000);
+
+ usleep(25000);
+ qb_util_stopwatch_split(sw);
+
+ /* ask for a split that has been overwritten.
+ */
+ res = qb_util_stopwatch_time_split_get(sw, last, 1);
+ ck_assert_int_eq(res, 0);
+
+ /* iterating
+ */
+ last = qb_util_stopwatch_split_last(sw);
+ do {
+ res = qb_util_stopwatch_time_split_get(sw, last, last);
+ qb_log(LOG_INFO, "overwrite split %d is %"PRIu64"", last, res);
+ last--;
+ } while (res > 0);
+
+ qb_util_stopwatch_free(sw);
+}
+END_TEST
+
+START_TEST(test_check_normal)
+{
+ uint64_t res;
+ uint32_t last;
+ qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+
+ qb_util_stopwatch_split_ctl(sw, 3, 0);
+
+ qb_util_stopwatch_start(sw);
+ usleep(33000);
+ /* 1 */
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 30000, 36000);
+ last = qb_util_stopwatch_split_last(sw);
+ ck_assert_int_eq(last, 0);
+
+ usleep(10000);
+ /* 2 */
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 9000, 11000);
+
+ usleep(20000);
+ /* 3 */
+ res = qb_util_stopwatch_split(sw);
+ assert_int_between(res, 19000, 21000);
+
+ /* no more space */
+ res = qb_util_stopwatch_split(sw);
+ ck_assert_int_eq(res, 0);
+
+ /*
+ * split should be 30000 (10000 + 20000) usec
+ */
+ last = qb_util_stopwatch_split_last(sw);
+ ck_assert_int_eq(last, 2);
+ res = qb_util_stopwatch_time_split_get(sw, last, 0);
+ assert_int_between(res, 25000, 35000);
+
+ /* ask for a split that has beyond the max.
+ */
+ res = qb_util_stopwatch_time_split_get(sw, 3, 2);
+ ck_assert_int_eq(res, 0);
+
+ /* iterating
+ */
+ last = qb_util_stopwatch_split_last(sw);
+ do {
+ res = qb_util_stopwatch_time_split_get(sw, last, last);
+ qb_log(LOG_INFO, "normal split %d is %"PRIu64"", last, res);
+ last--;
+ } while (res > 0);
+
+ qb_util_stopwatch_free(sw);
+}
+END_TEST
+
+static Suite *util_suite(void)
+{
+ TCase *tc;
+ Suite *s = suite_create("qb_util");
+
+ tc = tcase_create("overwrite");
+ tcase_add_test(tc, test_check_overwrite);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("normal");
+ tcase_add_test(tc, test_check_normal);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
+
+int32_t main(void)
+{
+ int32_t number_failed;
+
+ Suite *s = util_suite();
+ SRunner *sr = srunner_create(s);
+
+ qb_log_init("check", LOG_USER, LOG_EMERG);
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+ QB_LOG_FILTER_FILE, "*", LOG_INFO);
+ qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+ srunner_run_all(sr, CK_VERBOSE);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--
1.7.7.4