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@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@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@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; +}