mirror of
https://salsa.debian.org/ha-team/libqb
synced 2026-08-19 20:13:14 +00:00
Add a split timer to the stopwatch.
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>
This commit is contained in:
parent
86428907c7
commit
63bdfd1b71
@ -39,6 +39,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
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -131,6 +197,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)
|
||||
*/
|
||||
@ -145,7 +214,7 @@ void qb_util_stopwatch_free(qb_util_stopwatch_t *sw);
|
||||
* Start the stopwatch
|
||||
*
|
||||
* This also acts as a reset. Essentially it sets the
|
||||
* starting time.
|
||||
* starting time and clears the splits.
|
||||
*/
|
||||
void qb_util_stopwatch_start(qb_util_stopwatch_t *sw);
|
||||
|
||||
@ -172,6 +241,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 (in us) 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
|
||||
}
|
||||
|
||||
89
lib/util.c
89
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);
|
||||
}
|
||||
|
||||
@ -244,6 +249,7 @@ qb_util_stopwatch_start(qb_util_stopwatch_t * sw)
|
||||
{
|
||||
sw->started = qb_util_nano_current_get();
|
||||
sw->stopped = 0;
|
||||
sw->split_entries = 0;
|
||||
}
|
||||
|
||||
void
|
||||
@ -272,3 +278,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;
|
||||
}
|
||||
|
||||
@ -89,11 +89,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
|
||||
@ -120,6 +120,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:
|
||||
|
||||
190
tests/check_util.c
Normal file
190
tests/check_util.c
Normal file
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user