This API works by allowing the API user to keep a list of samples internally.
Every time a new sample is taken, the difference between the current sample and the first sample in the window is returned. When the sample array is filled, old entries drop off the sample list.
The use case where this is helpful is recovery escalation. Every time a failure occurs we take a sample. If the resulting window is less then the escalation window, an escalation should occur and the sample window should be reset.
Signed-off-by: Steven Dake sdake@redhat.com --- include/qb/qbutil.h | 25 +++++++++++++ lib/util.c | 64 ++++++++++++++++++++++++++++++++ tests/Makefile.am | 8 +++- tests/check_util.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tests/check_util.c
diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h index 2b450e9..d737ec2 100644 --- a/include/qb/qbutil.h +++ b/include/qb/qbutil.h @@ -171,6 +171,31 @@ 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);
+typedef struct qb_util_samplewindow qb_util_samplewindow_t; + +/** + * Create a timer sample window + */ +qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size); + +/** + * Free a timer sample window + */ +void qb_util_samplewindow_free(qb_util_samplewindow_t *sw); + +/** + * Get the elapsed time in micro seconds across the sample window. + * + * A return value of 0 indicates there have not been enough samples + * to determine the elapsed time. + */ +uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw); + +/* + * Reset the sample window + */ +void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw); + /* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/lib/util.c b/lib/util.c index 51c94bb..d9f11c6 100644 --- a/lib/util.c +++ b/lib/util.c @@ -272,3 +272,67 @@ qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw) return ((float)e6 / (float)QB_TIME_US_IN_SEC); }
+struct qb_util_samplewindow { + uint32_t size; + uint32_t entries; + uint64_t *entry_list; +}; + +/** + * Create a timer sample window + */ +qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size) +{ + struct qb_util_samplewindow *sw; + + sw = (struct qb_util_samplewindow *)calloc(1, sizeof(struct qb_util_stopwatch)); + sw->size = size; + sw->entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * size); + if (sw->entry_list == NULL) { + free (sw); + sw = NULL; + } + return sw; +} + +/** + * Free a timer sample window + */ +void qb_util_samplewindow_free(qb_util_samplewindow_t *sw) +{ + free(sw->entry_list); + free(sw); +} + +/** + * Get the elapsed time in micro seconds across the sample window. + * + * A return value of 0 indicates there have not been enough samples + * to determine the elapsed time. + */ +uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw) +{ + uint32_t new_entry_pos; + uint64_t time_start; + uint64_t time_end; + + new_entry_pos = sw->entries % (sw->size); + + sw->entry_list[new_entry_pos] = qb_util_nano_current_get(); + sw->entries++; + if (sw->entries < sw->size) { + return 0ULL; + } + + time_start = sw->entry_list[(new_entry_pos) % sw->size]; + time_end = sw->entry_list[(new_entry_pos + 1) % sw->size]; + return (time_start - time_end) / QB_TIME_NS_IN_USEC; +} + +/* + * Reset the sample window + */ +void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw) +{ + sw->entries = 0; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index d8f64e2..dd73363 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 rb.test log.test loop.test ipc.test resources.test util.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..0313325 --- /dev/null +++ b/tests/check_util.c @@ -0,0 +1,100 @@ +/* + * 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> + +START_TEST(test_check_samplewindow) +{ + qb_util_samplewindow_t *sw; + uint64_t res; + + sw = qb_util_samplewindow_create(5); + res = qb_util_samplewindow_sample(sw); + ck_assert_int_eq(res, 0ULL); + + usleep(10000); + res = qb_util_samplewindow_sample(sw); + ck_assert_int_eq(res, 0ULL); + + usleep(20000); + res = qb_util_samplewindow_sample(sw); + ck_assert_int_eq(res, 0ULL); + + usleep(30000); + res = qb_util_samplewindow_sample(sw); + ck_assert_int_eq(res, 0ULL); + + usleep(40000); + res = qb_util_samplewindow_sample(sw); + /* + * window should be 100000 (40000 + 30000 + 20000 + 10000) usec + */ + ck_assert(res > 95000); + ck_assert(res < 105000); + + usleep(50000); + res = qb_util_samplewindow_sample(sw); + /* + * window should be 140000 (50000 + 40000 + 30000 + 20000) usec + */ + ck_assert(res > 135000); + ck_assert(res < 145000); + +} +END_TEST + +static Suite *util_suite(void) +{ + TCase *tc; + Suite *s = suite_create("qb_util"); + + tc = tcase_create("properop"); + tcase_add_test(tc, test_check_samplewindow); + 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; +}
On 26/12/11 15:36 -0700, Steven Dake wrote:
This API works by allowing the API user to keep a list of samples internally.
Nice idea.
Every time a new sample is taken, the difference between the current sample and the first sample in the window is returned. When the sample array is filled, old entries drop off the sample list.
The use case where this is helpful is recovery escalation. Every time a failure occurs we take a sample. If the resulting window is less then the escalation window, an escalation should occur and the sample window should be reset.
Is it not worth seperating the sampling (writing a new sample) and reading the sample?
This seems to be a logical extension of the stopwatch (but with splits). The problem with the current API is you can't go back and re-read the split times (write only API).
What you think?
-A
Signed-off-by: Steven Dake sdake@redhat.com
include/qb/qbutil.h | 25 +++++++++++++ lib/util.c | 64 ++++++++++++++++++++++++++++++++ tests/Makefile.am | 8 +++- tests/check_util.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tests/check_util.c
diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h index 2b450e9..d737ec2 100644 --- a/include/qb/qbutil.h +++ b/include/qb/qbutil.h @@ -171,6 +171,31 @@ 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);
+typedef struct qb_util_samplewindow qb_util_samplewindow_t;
+/**
- Create a timer sample window
- */
+qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size);
+/**
- Free a timer sample window
- */
+void qb_util_samplewindow_free(qb_util_samplewindow_t *sw);
+/**
- Get the elapsed time in micro seconds across the sample window.
- A return value of 0 indicates there have not been enough samples
- to determine the elapsed time.
- */
+uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw);
+/*
- Reset the sample window
- */
+void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw);
/* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/lib/util.c b/lib/util.c index 51c94bb..d9f11c6 100644 --- a/lib/util.c +++ b/lib/util.c @@ -272,3 +272,67 @@ qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw) return ((float)e6 / (float)QB_TIME_US_IN_SEC); }
+struct qb_util_samplewindow {
- uint32_t size;
- uint32_t entries;
- uint64_t *entry_list;
+};
+/**
- Create a timer sample window
- */
+qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size) +{
- struct qb_util_samplewindow *sw;
- sw = (struct qb_util_samplewindow *)calloc(1, sizeof(struct qb_util_stopwatch));
- sw->size = size;
- sw->entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * size);
- if (sw->entry_list == NULL) {
free (sw);
sw = NULL;
- }
- return sw;
+}
+/**
- Free a timer sample window
- */
+void qb_util_samplewindow_free(qb_util_samplewindow_t *sw) +{
- free(sw->entry_list);
- free(sw);
+}
+/**
- Get the elapsed time in micro seconds across the sample window.
- A return value of 0 indicates there have not been enough samples
- to determine the elapsed time.
- */
+uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw) +{
- uint32_t new_entry_pos;
- uint64_t time_start;
- uint64_t time_end;
- new_entry_pos = sw->entries % (sw->size);
- sw->entry_list[new_entry_pos] = qb_util_nano_current_get();
- sw->entries++;
- if (sw->entries < sw->size) {
return 0ULL;
- }
- time_start = sw->entry_list[(new_entry_pos) % sw->size];
- time_end = sw->entry_list[(new_entry_pos + 1) % sw->size];
- return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
+/*
- Reset the sample window
- */
+void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw) +{
- sw->entries = 0;
+} diff --git a/tests/Makefile.am b/tests/Makefile.am index d8f64e2..dd73363 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 rb.test log.test loop.test ipc.test resources.test util.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..0313325 --- /dev/null +++ b/tests/check_util.c @@ -0,0 +1,100 @@ +/*
- 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>
+START_TEST(test_check_samplewindow) +{
- qb_util_samplewindow_t *sw;
- uint64_t res;
- sw = qb_util_samplewindow_create(5);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(10000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(20000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(30000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(40000);
- res = qb_util_samplewindow_sample(sw);
- /*
* window should be 100000 (40000 + 30000 + 20000 + 10000) usec
*/
- ck_assert(res > 95000);
- ck_assert(res < 105000);
- usleep(50000);
- res = qb_util_samplewindow_sample(sw);
- /*
* window should be 140000 (50000 + 40000 + 30000 + 20000) usec
*/
- ck_assert(res > 135000);
- ck_assert(res < 145000);
+} +END_TEST
+static Suite *util_suite(void) +{
- TCase *tc;
- Suite *s = suite_create("qb_util");
- tc = tcase_create("properop");
- tcase_add_test(tc, test_check_samplewindow);
- 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
quarterback-devel mailing list quarterback-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/quarterback-devel
On 12/29/2011 05:37 AM, Angus Salkeld wrote:
On 26/12/11 15:36 -0700, Steven Dake wrote:
This API works by allowing the API user to keep a list of samples internally.
Nice idea.
Every time a new sample is taken, the difference between the current sample and the first sample in the window is returned. When the sample array is filled, old entries drop off the sample list.
The use case where this is helpful is recovery escalation. Every time a failure occurs we take a sample. If the resulting window is less then the escalation window, an escalation should occur and the sample window should be reset.
Is it not worth seperating the sampling (writing a new sample) and reading the sample?
Possible. Rationale behind reading the sample while writing a new sample is the following:
failure event occurs: interval = sample() if (interval && interval > limit) { escalate(); }
Just seemed simpler for that particular use case (ie: lazy..:)
There may be a better way to organize the code, with different names, etc. Open to suggestions (or you can ammend the patch as you like).
Also if you think this is not appropriate for libqb, we can merge the code elsewhere.
This seems to be a logical extension of the stopwatch (but with splits). The problem with the current API is you can't go back and re-read the split times (write only API).
Yes I was thinking of how to merge it into stopwatch, but couldn't come up with anything clever.
Agree can't reread the intervals.
One option may be something like
util_samplewindow_read (uint32_t interval_start, uint32_t interval_end)
which would read the time difference between those two different points. Not sure this is easy to use. Maybe you have better idea.
What you think?
Well now we know what we want and have an idea of how it could work - can have discussion on best way to merge into the API.
Regards -steve
-A
Signed-off-by: Steven Dake sdake@redhat.com
include/qb/qbutil.h | 25 +++++++++++++ lib/util.c | 64 ++++++++++++++++++++++++++++++++ tests/Makefile.am | 8 +++- tests/check_util.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tests/check_util.c
diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h index 2b450e9..d737ec2 100644 --- a/include/qb/qbutil.h +++ b/include/qb/qbutil.h @@ -171,6 +171,31 @@ 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);
+typedef struct qb_util_samplewindow qb_util_samplewindow_t;
+/**
- Create a timer sample window
- */
+qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size);
+/**
- Free a timer sample window
- */
+void qb_util_samplewindow_free(qb_util_samplewindow_t *sw);
+/**
- Get the elapsed time in micro seconds across the sample window.
- A return value of 0 indicates there have not been enough samples
- to determine the elapsed time.
- */
+uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw);
+/*
- Reset the sample window
- */
+void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw);
/* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/lib/util.c b/lib/util.c index 51c94bb..d9f11c6 100644 --- a/lib/util.c +++ b/lib/util.c @@ -272,3 +272,67 @@ qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw) return ((float)e6 / (float)QB_TIME_US_IN_SEC); }
+struct qb_util_samplewindow {
- uint32_t size;
- uint32_t entries;
- uint64_t *entry_list;
+};
+/**
- Create a timer sample window
- */
+qb_util_samplewindow_t * qb_util_samplewindow_create(uint32_t size) +{
- struct qb_util_samplewindow *sw;
- sw = (struct qb_util_samplewindow *)calloc(1, sizeof(struct
qb_util_stopwatch));
- sw->size = size;
- sw->entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * size);
- if (sw->entry_list == NULL) {
free (sw);
sw = NULL;
- }
- return sw;
+}
+/**
- Free a timer sample window
- */
+void qb_util_samplewindow_free(qb_util_samplewindow_t *sw) +{
- free(sw->entry_list);
- free(sw);
+}
+/**
- Get the elapsed time in micro seconds across the sample window.
- A return value of 0 indicates there have not been enough samples
- to determine the elapsed time.
- */
+uint64_t qb_util_samplewindow_sample(qb_util_samplewindow_t *sw) +{
- uint32_t new_entry_pos;
- uint64_t time_start;
- uint64_t time_end;
- new_entry_pos = sw->entries % (sw->size);
- sw->entry_list[new_entry_pos] = qb_util_nano_current_get();
- sw->entries++;
- if (sw->entries < sw->size) {
return 0ULL;
- }
- time_start = sw->entry_list[(new_entry_pos) % sw->size];
- time_end = sw->entry_list[(new_entry_pos + 1) % sw->size];
- return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
+/*
- Reset the sample window
- */
+void qb_util_samplewindow_reset(qb_util_samplewindow_t *sw) +{
- sw->entries = 0;
+} diff --git a/tests/Makefile.am b/tests/Makefile.am index d8f64e2..dd73363 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 rb.test log.test loop.test ipc.test resources.test util.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..0313325 --- /dev/null +++ b/tests/check_util.c @@ -0,0 +1,100 @@ +/*
- 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>
+START_TEST(test_check_samplewindow) +{
- qb_util_samplewindow_t *sw;
- uint64_t res;
- sw = qb_util_samplewindow_create(5);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(10000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(20000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(30000);
- res = qb_util_samplewindow_sample(sw);
- ck_assert_int_eq(res, 0ULL);
- usleep(40000);
- res = qb_util_samplewindow_sample(sw);
- /*
* window should be 100000 (40000 + 30000 + 20000 + 10000) usec
*/
- ck_assert(res > 95000);
- ck_assert(res < 105000);
- usleep(50000);
- res = qb_util_samplewindow_sample(sw);
- /*
* window should be 140000 (50000 + 40000 + 30000 + 20000) usec
*/
- ck_assert(res > 135000);
- ck_assert(res < 145000);
+} +END_TEST
+static Suite *util_suite(void) +{
- TCase *tc;
- Suite *s = suite_create("qb_util");
- tc = tcase_create("properop");
- tcase_add_test(tc, test_check_samplewindow);
- 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
quarterback-devel mailing list quarterback-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/quarterback-devel
quarterback-devel mailing list quarterback-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/quarterback-devel
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; +}
quarterback-devel@lists.fedorahosted.org