diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a36fb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +target +.idea +out +gh-pages +HdrHistogram.iml +.classpath +.project +.settings +release.properties +/bin +alltests +format_example +*.a +*.iml +.sconsign.dblite +*.o +*.os +.DS_Store +build +CMakeCache.txt +CMakeFiles/ +CPackConfig.cmake +CPackSourceConfig.cmake +CTestTestfile.cmake +Makefile +Testing/ +cmake_install.cmake +examples/CMakeFiles/ +examples/CTestTestfile.cmake +examples/Makefile +examples/cmake_install.cmake +examples/hdr_decoder +examples/hiccup +src/CMakeFiles/ +src/CTestTestfile.cmake +src/Makefile +src/cmake_install.cmake +src/libhdr_histogram.so +test/CMakeFiles/ +test/CTestTestfile.cmake +test/Makefile +test/cmake_install.cmake +test/hdr_dbl_histogram_test +test/hdr_histogram_log_test +test/hdr_histogram_test +test/perftest +.gdb_history +debug +cmake-build-debug +/build-afl diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d6a09e9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/vendor/google/benchmark"] + path = test/vendor/google/benchmark + url = https://github.com/google/benchmark.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..36e0fa2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 2.8) + +if("${CMAKE_VERSION}" VERSION_GREATER 3.0.0) + cmake_policy(SET CMP0042 NEW) +endif() + +project("hdr_histogram") + +# Follow all steps below in order to calculate new ABI version when updating the library +# NOTE: THIS IS UNRELATED to the actual project version +# +# 1. If the library source code has changed at all since the last update, then increment revision +# 2. If any interfaces have been added, removed, or changed since the last update, increment current and set revision to 0. +# 3. If any interfaces have been added since the last public release, then increment age. +# 4. If any interfaces have been removed since the last public release, then set age to 0. + +set(HDR_SOVERSION_CURRENT 5) +set(HDR_SOVERSION_AGE 0) +set(HDR_SOVERSION_REVISION 0) + +set(HDR_VERSION ${HDR_SOVERSION_CURRENT}.${HDR_SOVERSION_AGE}.${HDR_SOVERSION_REVISION}) +set(HDR_SOVERSION ${HDR_SOVERSION_CURRENT}) + +ENABLE_TESTING() + +if(UNIX) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unknown-pragmas -Wextra -Wshadow -Winit-self -Wmissing-prototypes -Wpedantic -D_GNU_SOURCE -std=gnu89 -fPIC") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -g") +endif() + +if(WIN32) + find_package (zlib REQUIRED) +else() + find_package (ZLIB REQUIRED) +endif() + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src") + +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src") + +option(HDR_HISTOGRAM_BUILD_PROGRAMS "Build tests and examples" ON) +if(HDR_HISTOGRAM_BUILD_PROGRAMS) + add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/test") + add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/examples") +endif(HDR_HISTOGRAM_BUILD_PROGRAMS) + +SET(CPACK_GENERATOR "TGZ") +SET(CPACK_PACKAGE_VERSION "0.9.12") + +INCLUDE(CPack) diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9b4e66e --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,41 @@ +The code in this repository code was Written by Gil Tene, Michael Barker, +and Matt Warren, and released to the public domain, as explained at +http://creativecommons.org/publicdomain/zero/1.0/ + +For users of this code who wish to consume it under the "BSD" license +rather than under the public domain or CC0 contribution text mentioned +above, the code found under this directory is *also* provided under the +following license (commonly referred to as the BSD 2-Clause License). This +license does not detract from the above stated release of the code into +the public domain, and simply represents an additional license granted by +the Author. + +----------------------------------------------------------------------------- +** Beginning of "BSD 2-Clause License" text. ** + + Copyright (c) 2012, 2013, 2014 Gil Tene + Copyright (c) 2014 Michael Barker + Copyright (c) 2014 Matt Warren + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8667a6f --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +HdrHistogram_c: 'C' port of High Dynamic Range (HDR) Histogram + +HdrHistogram +---------------------------------------------- + +[![Gitter chat](https://badges.gitter.im/HdrHistogram/HdrHistogram.png)](https://gitter.im/HdrHistogram/HdrHistogram) + +Windows Build: [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/HdrHistogram/HdrHistogram_c?svg=true)](https://ci.appveyor.com/project/mikeb01/hdrhistogram-c) + +Linux Build: [![Build Status](https://semaphoreci.com/api/v1/mikeb01/hdrhistogram_c/branches/master/badge.svg)](https://semaphoreci.com/mikeb01/hdrhistogram_c) + +This port contains a subset of the functionality supported by the Java +implementation. The current supported features are: + +* Standard histogram with 64 bit counts (32/16 bit counts not supported) +* All iterator types (all values, recorded, percentiles, linear, logarithmic) +* Histogram serialisation (encoding version 1.2, decoding 1.0-1.2) +* Reader/writer phaser and interval recorder + +Features not supported, but planned + +* Auto-resizing of histograms + +Features unlikely to be implemented + +* Double histograms +* Atomic/Concurrent histograms +* 16/32 bit histograms + +# Simple Tutorial + +## Recording values + +```C +#include + +struct hdr_histogram* histogram; + +// Initialise the histogram +hdr_init( + 1, // Minimum value + INT64_C(3600000000), // Maximum value + 3, // Number of significant figures + &histogram) // Pointer to initialise + +// Record value +hdr_record_value( + histogram, // Histogram to record to + value) // Value to record + +// Record value n times +hdr_record_values( + histogram, // Histogram to record to + value, // Value to record + 10) // Record value 10 times + +// Record value with correction for co-ordinated omission. +hdr_record_corrected_value( + histogram, // Histogram to record to + value, // Value to record + 1000) // Record with expected interval of 1000. + +// Print out the values of the histogram +hdr_percentiles_print( + histogram, + stdout, // File to write to + 5, // Granularity of printed values + 1.0, // Multiplier for results + CLASSIC); // Format CLASSIC/CSV supported. +``` + +## More examples + +For more detailed examples of recording and logging results look at the +[hdr_decoder](examples/hdr_decoder.c) +and [hiccup](examples/hiccup.c) +examples. You can run hiccup and decoder +and pipe the results of one into the other. + +``` +$ ./examples/hiccup | ./examples/hdr_decoder +``` diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..3d2b77e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,40 @@ +version: 0.9.12.{build} + +platform: + - Win32 + - x64 + +matrix: + allow_failures: + - platform: Win32 + +clone_folder: c:\dev\HdrHistogram_c + +install: + - cd c:\dev + - md c:\dev\zlib64 + - ps: Start-FileDownload 'http://zlib.net/zlib-1.2.11.tar.gz' + - 7z x zlib-1.2.11.tar.gz + - 7z x zlib-1.2.11.tar + +build: + +build_script: + - cd c:\dev\zlib-1.2.11 + - md build + - cd build + - if %PLATFORM% == Win32 (set GENERATOR="Visual Studio 12 2013") + - if %PLATFORM% == x64 (set GENERATOR="Visual Studio 12 2013 Win64") + - echo Using %GENERATOR% + - cmake -DCMAKE_INSTALL_PREFIX=c:\dev\zlib64 -G %GENERATOR% .. + - cmake --build . --target install + - dir c:\dev\zlib64 + - cd c:\dev\HdrHistogram_c + - git submodule update --init --recursive + - md build + - cd build + - cmake -DCMAKE_PREFIX_PATH="c:\dev\zlib64" -G %GENERATOR% .. + - cmake --build . + +test_script: +# - cmd: ctest diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..f7a571b --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(hdr_decoder hdr_decoder.c) +if (WIN32) + target_link_libraries(hdr_decoder hdr_histogram_static) +else() + target_link_libraries(hdr_decoder hdr_histogram m z) +endif() + +CHECK_LIBRARY_EXISTS(rt clock_gettime "" RT_EXISTS) +if (RT_EXISTS) + target_link_libraries(hdr_decoder rt) +endif (RT_EXISTS) + +install(TARGETS hdr_decoder DESTINATION bin) + +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + add_executable(hiccup hiccup.c) + target_link_libraries(hiccup hdr_histogram m z pthread rt) + install(TARGETS hiccup DESTINATION bin) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") diff --git a/examples/hdr_decoder.c b/examples/hdr_decoder.c new file mode 100644 index 0000000..4360e70 --- /dev/null +++ b/examples/hdr_decoder.c @@ -0,0 +1,82 @@ +/** + * hdr_decoder.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + +int main(int argc, char** argv) +{ + int rc = 0; + FILE* f; + struct hdr_log_reader reader; + struct hdr_histogram* h = NULL; + hdr_timespec timestamp, interval; + + if (argc == 1) + { + f = stdin; + } + else + { + f = fopen(argv[1], "r"); + } + + if (!f) + { + fprintf(stderr, "Failed to open file(%s):%s\n", argv[1], strerror(errno)); + return -1; + } + + if (hdr_log_reader_init(&reader)) + { + fprintf(stderr, "Failed to init reader\n"); + return -1; + } + + rc = hdr_log_read_header(&reader, f); + if(rc) + { + fprintf(stderr, "Failed to read header: %s\n", hdr_strerror(rc)); + return -1; + } + + while (true) + { + rc = hdr_log_read(&reader, f, &h, ×tamp, &interval); + + if (0 == rc) + { + hdr_percentiles_print(h, stdout, 5, 1.0, CLASSIC); + } + else if (EOF == rc) + { + break; + } + else + { + fprintf(stderr, "Failed to print histogram: %s\n", hdr_strerror(rc)); + return -1; + } + } + + return 0; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/examples/hiccup.c b/examples/hiccup.c new file mode 100644 index 0000000..e412420 --- /dev/null +++ b/examples/hiccup.c @@ -0,0 +1,186 @@ +/** + * hiccup.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int64_t diff(struct timespec t0, struct timespec t1) +{ + int64_t delta_us = 0; + delta_us = (t1.tv_sec - t0.tv_sec) * 1000000; + delta_us += (t1.tv_nsec - t0.tv_nsec) / 1000; + + return delta_us; +} + +static void* record_hiccups(void* thread_context) +{ + struct pollfd fd; + struct timespec t0; + struct timespec t1; + struct itimerspec timeout; + struct hdr_interval_recorder* r = thread_context; + + memset(&fd, 0, sizeof(struct pollfd)); + memset(&timeout, 0, sizeof(struct itimerspec)); + memset(&t0, 0, sizeof(struct timespec)); + memset(&t1, 0, sizeof(struct timespec)); + + fd.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + fd.events = POLLIN|POLLPRI|POLLRDHUP; + fd.revents = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + while (true) + { + int64_t delta_us; + + timeout.it_value.tv_sec = 0; + timeout.it_value.tv_nsec = 1000000; + timerfd_settime(fd.fd, 0, &timeout, NULL); + + hdr_gettime(&t0); + poll(&fd, 1, -1); + hdr_gettime(&t1); + + delta_us = diff(t0, t1) - 1000; + delta_us = delta_us < 0 ? 0 : delta_us; + + hdr_interval_recorder_record_value(r, delta_us); + } +#pragma clang diagnostic pop + + pthread_exit(NULL); +} + +struct config_t +{ + unsigned int interval; + const char* filename; +}; + +const char* USAGE = +"hiccup [-i ] [-f ]\n" +" interval: Time in seconds between samples (default 1).\n" +" filename: Name of the file to log to (default stdout).\n"; + +static int handle_opts(int argc, char** argv, struct config_t* config) +{ + int c; + unsigned int interval = 1; + + while ((c = getopt(argc, argv, "i:f:")) != -1) + { + switch (c) + { + case 'h': + return 0; + + case 'i': + interval = (unsigned int) strtoul(optarg, NULL, 10); + if (interval < 1) + { + return 0; + } + + break; + case 'f': + config->filename = optarg; + break; + default: + return 0; + } + } + + config->interval = interval < 1 ? 1 : interval; + return 1; +} + +int main(int argc, char** argv) +{ + struct timespec timestamp; + struct timespec start_timestamp; + struct timespec end_timestamp; + struct hdr_interval_recorder recorder; + struct hdr_log_writer log_writer; + struct config_t config; + struct hdr_histogram* inactive = NULL; + pthread_t recording_thread; + FILE* output = stdout; + + memset(&config, 0, sizeof(struct config_t)); + if (!handle_opts(argc, argv, &config)) + { + printf("%s", USAGE); + return 0; + } + + if (config.filename) + { + output = fopen(config.filename, "a+"); + if (!output) + { + fprintf( + stderr, "Failed to open/create file: %s, %s", + config.filename, strerror(errno)); + + return -1; + } + } + + if (0 != hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3)) + { + fprintf(stderr, "%s\n", "Failed to init phaser"); + return -1; + } + + if (pthread_create(&recording_thread, NULL, record_hiccups, &recorder)) + { + fprintf(stderr, "%s\n", "Failed to create thread"); + return -1; + } + + hdr_gettime(&start_timestamp); + hdr_getnow(×tamp); + hdr_log_writer_init(&log_writer); + hdr_log_write_header(&log_writer, output, "foobar", ×tamp); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + while (true) + { + sleep(config.interval); + + inactive = hdr_interval_recorder_sample_and_recycle(&recorder, inactive); + + hdr_gettime(&end_timestamp); + timestamp = start_timestamp; + + hdr_gettime(&start_timestamp); + + hdr_log_write(&log_writer, output, ×tamp, &end_timestamp, inactive); + fflush(output); + } +#pragma clang diagnostic pop + + pthread_exit(NULL); +} diff --git a/ide/codestyle.jar b/ide/codestyle.jar new file mode 100644 index 0000000..dcbc17a Binary files /dev/null and b/ide/codestyle.jar differ diff --git a/lib/benchmark-1.5.0.zip b/lib/benchmark-1.5.0.zip new file mode 100644 index 0000000..829467c Binary files /dev/null and b/lib/benchmark-1.5.0.zip differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..222e158 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,33 @@ +FILE(GLOB histogram_files "*.c") +FILE(GLOB HEADER "*.h") + +option(HDR_HISTOGRAM_BUILD_STATIC "Build static library" ON) +option(HDR_HISTOGRAM_BUILD_SHARED "Build shared library" ON) + +if (HDR_HISTOGRAM_BUILD_SHARED) + add_library(hdr_histogram SHARED ${histogram_files} ${HEADER}) + if (WIN32) + set_target_properties(hdr_histogram PROPERTIES VERSION ${HDR_VERSION}) + target_link_libraries(hdr_histogram ws2_32) + else () + target_link_libraries(hdr_histogram m) + set_target_properties(hdr_histogram PROPERTIES VERSION ${HDR_VERSION} SOVERSION ${HDR_SOVERSION}) + endif () + target_link_libraries(hdr_histogram ${ZLIB_LIBRARIES}) + target_include_directories(hdr_histogram SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${ZLIB_INCLUDE_DIRS}) + install(TARGETS hdr_histogram DESTINATION lib${LIB_SUFFIX}) +endif (HDR_HISTOGRAM_BUILD_SHARED) + +if (HDR_HISTOGRAM_BUILD_STATIC) + add_library(hdr_histogram_static STATIC ${histogram_files} ${HEADER}) + if (WIN32) + target_link_libraries(hdr_histogram_static ws2_32) + else () + target_link_libraries(hdr_histogram_static m) + endif () + target_link_libraries(hdr_histogram_static ${ZLIB_LIBRARIES}) + target_include_directories(hdr_histogram_static SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${ZLIB_INCLUDE_DIRS}) + install(TARGETS hdr_histogram_static DESTINATION lib${LIB_SUFFIX}) +endif (HDR_HISTOGRAM_BUILD_STATIC) + +install(FILES hdr_histogram.h hdr_histogram_log.h hdr_time.h hdr_writer_reader_phaser.h hdr_interval_recorder.h hdr_thread.h DESTINATION include/hdr) diff --git a/src/hdr_atomic.h b/src/hdr_atomic.h new file mode 100644 index 0000000..ae1056a --- /dev/null +++ b/src/hdr_atomic.h @@ -0,0 +1,146 @@ +/** + * hdr_atomic.h + * Written by Philip Orwig and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_ATOMIC_H__ +#define HDR_ATOMIC_H__ + + +#if defined(_MSC_VER) + +#include +#include +#include + +static void __inline * hdr_atomic_load_pointer(void** pointer) +{ + _ReadBarrier(); + return *pointer; +} + +static void hdr_atomic_store_pointer(void** pointer, void* value) +{ + _WriteBarrier(); + *pointer = value; +} + +static int64_t __inline hdr_atomic_load_64(int64_t* field) +{ + _ReadBarrier(); + return *field; +} + +static void __inline hdr_atomic_store_64(int64_t* field, int64_t value) +{ + _WriteBarrier(); + *field = value; +} + +static int64_t __inline hdr_atomic_exchange_64(volatile int64_t* field, int64_t value) +{ +#if defined(_WIN64) + return _InterlockedExchange64(field, value); +#else + int64_t comparand; + int64_t initial_value = *field; + do + { + comparand = initial_value; + initial_value = _InterlockedCompareExchange64(field, value, comparand); + } + while (comparand != initial_value); + + return initial_value; +#endif +} + +static int64_t __inline hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value) +{ +#if defined(_WIN64) + return _InterlockedExchangeAdd64(field, value) + value; +#else + int64_t comparand; + int64_t initial_value = *field; + do + { + comparand = initial_value; + initial_value = _InterlockedCompareExchange64(field, comparand + value, comparand); + } + while (comparand != initial_value); + + return initial_value + value; +#endif +} + +static bool __inline hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired) +{ + return *expected == _InterlockedCompareExchange64(field, desired, *expected); +} + +#elif defined(__ATOMIC_SEQ_CST) + +#define hdr_atomic_load_pointer(x) __atomic_load_n(x, __ATOMIC_SEQ_CST) +#define hdr_atomic_store_pointer(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST) +#define hdr_atomic_load_64(x) __atomic_load_n(x, __ATOMIC_SEQ_CST) +#define hdr_atomic_store_64(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST) +#define hdr_atomic_exchange_64(f,i) __atomic_exchange_n(f,i, __ATOMIC_SEQ_CST) +#define hdr_atomic_add_fetch_64(field, value) __atomic_add_fetch(field, value, __ATOMIC_SEQ_CST) +#define hdr_atomic_compare_exchange_64(field, expected, desired) __atomic_compare_exchange_n(field, expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#elif defined(__x86_64__) + +#include +#include + +static inline void* hdr_atomic_load_pointer(void** pointer) +{ + void* p = *pointer; + asm volatile ("" ::: "memory"); + return p; +} + +static inline void hdr_atomic_store_pointer(void** pointer, void* value) +{ + asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*pointer)); +} + +static inline int64_t hdr_atomic_load_64(int64_t* field) +{ + int64_t i = *field; + asm volatile ("" ::: "memory"); + return i; +} + +static inline void hdr_atomic_store_64(int64_t* field, int64_t value) +{ + asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*field)); +} + +static inline int64_t hdr_atomic_exchange_64(volatile int64_t* field, int64_t value) +{ + int64_t result = 0; + asm volatile ("lock; xchgq %1, %2" : "=r" (result), "+q" (value), "+m" (*field)); + return result; +} + +static inline int64_t hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value) +{ + return __sync_add_and_fetch(field, value); +} + +static inline bool hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired) +{ + int64_t original; + asm volatile( "lock; cmpxchgq %2, %1" : "=a"(original), "+m"(*field) : "q"(desired), "0"(*expected)); + return original == *expected; +} + +#else + +#error "Unable to determine atomic operations for your platform" + +#endif + +#endif /* HDR_ATOMIC_H__ */ diff --git a/src/hdr_encoding.c b/src/hdr_encoding.c new file mode 100644 index 0000000..cb70031 --- /dev/null +++ b/src/hdr_encoding.c @@ -0,0 +1,317 @@ +/** + * hdr_encoding.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include + +#include "hdr_encoding.h" +#include "hdr_tests.h" + +int zig_zag_encode_i64(uint8_t* buffer, int64_t signed_value) +{ + int bytesWritten; + int64_t value = signed_value; + + value = (value << 1) ^ (value >> 63); + if (value >> 7 == 0) + { + buffer[0] = (uint8_t) value; + bytesWritten = 1; + } + else + { + buffer[0] = (uint8_t) ((value & 0x7F) | 0x80); + if (value >> 14 == 0) + { + buffer[1] = (uint8_t) (value >> 7); + bytesWritten = 2; + } + else + { + buffer[1] = (uint8_t) ((value >> 7 | 0x80)); + if (value >> 21 == 0) + { + buffer[2] = (uint8_t) (value >> 14); + bytesWritten = 3; + } + else + { + buffer[2] = (uint8_t) (value >> 14 | 0x80); + if (value >> 28 == 0) + { + buffer[3] = (uint8_t) (value >> 21); + bytesWritten = 4; + } + else + { + buffer[3] = (uint8_t) (value >> 21 | 0x80); + if (value >> 35 == 0) + { + buffer[4] = (uint8_t) (value >> 28); + bytesWritten = 5; + } + else + { + buffer[4] = (uint8_t) (value >> 28 | 0x80); + if (value >> 42 == 0) + { + buffer[5] = (uint8_t) (value >> 35); + bytesWritten = 6; + } + else + { + buffer[5] = (uint8_t) (value >> 35 | 0x80); + if (value >> 49 == 0) + { + buffer[6] = (uint8_t) (value >> 42); + bytesWritten = 7; + } + else + { + buffer[6] = (uint8_t) (value >> 42 | 0x80); + if (value >> 56 == 0) + { + buffer[7] = (uint8_t) (value >> 49); + bytesWritten = 8; + } + else + { + buffer[7] = (uint8_t) (value >> 49 | 0x80); + buffer[8] = (uint8_t) (value >> 56); + bytesWritten = 9; + } + } + } + } + } + } + } + } + + return bytesWritten; +} + +int zig_zag_decode_i64(const uint8_t* buffer, int64_t* signed_value) +{ + uint64_t v = buffer[0]; + uint64_t value = v & 0x7F; + int bytesRead = 1; + if ((v & 0x80) != 0) + { + bytesRead = 2; + v = buffer[1]; + value |= (v & 0x7F) << 7; + if ((v & 0x80) != 0) + { + bytesRead = 3; + v = buffer[2]; + value |= (v & 0x7F) << 14; + if ((v & 0x80) != 0) + { + bytesRead = 4; + v = buffer[3]; + value |= (v & 0x7F) << 21; + if ((v & 0x80) != 0) + { + bytesRead = 5; + v = buffer[4]; + value |= (v & 0x7F) << 28; + if ((v & 0x80) != 0) + { + bytesRead = 6; + v = buffer[5]; + value |= (v & 0x7F) << 35; + if ((v & 0x80) != 0) + { + bytesRead = 7; + v = buffer[6]; + value |= (v & 0x7F) << 42; + if ((v & 0x80) != 0) + { + bytesRead = 8; + v = buffer[7]; + value |= (v & 0x7F) << 49; + if ((v & 0x80) != 0) + { + bytesRead = 9; + v = buffer[8]; + value |= v << 56; + } + } + } + } + } + } + } + } + +#pragma warning(disable: 4146) + value = (value >> 1) ^ (-(value & 1)); +#pragma warning(default: 4146) + *signed_value = (int64_t) value; + + return bytesRead; +} + +static const char base64_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' + }; + +static char get_base_64(uint32_t _24_bit_value, int shift) +{ + uint32_t _6_bit_value = 0x3F & (_24_bit_value >> shift); + return base64_table[_6_bit_value]; +} + +static int from_base_64(int c) +{ + if ('A' <= c && c <= 'Z') + { + return c - 'A'; + } + else if ('a' <= c && c <= 'z') + { + return (c - 'a') + 26; + } + else if ('0' <= c && c <= '9') + { + return (c - '0') + 52; + } + else if ('+' == c) + { + return 62; + } + else if ('/' == c) + { + return 63; + } + else if ('=' == c) + { + return 0; + } + + return EINVAL; +} + +size_t hdr_base64_encoded_len(size_t decoded_size) +{ + return (size_t) (ceil(decoded_size / 3.0) * 4.0); +} + +size_t hdr_base64_decoded_len(size_t encoded_size) +{ + return (encoded_size / 4) * 3; +} + +static void hdr_base64_encode_block_pad(const uint8_t* input, char* output, size_t pad) +{ + uint32_t _24_bit_value = 0; + + switch (pad) + { + case 2: + _24_bit_value = (input[0] << 16) + (input[1] << 8); + + output[0] = get_base_64(_24_bit_value, 18); + output[1] = get_base_64(_24_bit_value, 12); + output[2] = get_base_64(_24_bit_value, 6); + output[3] = '='; + + break; + + case 1: + _24_bit_value = (input[0] << 16); + + output[0] = get_base_64(_24_bit_value, 18); + output[1] = get_base_64(_24_bit_value, 12); + output[2] = '='; + output[3] = '='; + + break; + + default: + /* No-op */ + break; + } +} + +/** + * Assumes that there is 3 input bytes and 4 output chars. + */ +void hdr_base64_encode_block(const uint8_t* input, char* output) +{ + uint32_t _24_bit_value = (input[0] << 16) + (input[1] << 8) + (input[2]); + + output[0] = get_base_64(_24_bit_value, 18); + output[1] = get_base_64(_24_bit_value, 12); + output[2] = get_base_64(_24_bit_value, 6); + output[3] = get_base_64(_24_bit_value, 0); +} + +int hdr_base64_encode( + const uint8_t* input, size_t input_len, char* output, size_t output_len) +{ + size_t i, j, remaining; + + if (hdr_base64_encoded_len(input_len) != output_len) + { + return EINVAL; + } + + for (i = 0, j = 0; input_len - i >= 3 && j < output_len; i += 3, j += 4) + { + hdr_base64_encode_block(&input[i], &output[j]); + } + + remaining = input_len - i; + + hdr_base64_encode_block_pad(&input[i], &output[j], remaining); + + return 0; +} + +/** + * Assumes that there is 4 input chars available and 3 output chars. + */ +void hdr_base64_decode_block(const char* input, uint8_t* output) +{ + uint32_t _24_bit_value = 0; + + _24_bit_value |= from_base_64(input[0]) << 18; + _24_bit_value |= from_base_64(input[1]) << 12; + _24_bit_value |= from_base_64(input[2]) << 6; + _24_bit_value |= from_base_64(input[3]); + + output[0] = (uint8_t) ((_24_bit_value >> 16) & 0xFF); + output[1] = (uint8_t) ((_24_bit_value >> 8) & 0xFF); + output[2] = (uint8_t) ((_24_bit_value) & 0xFF); +} + +int hdr_base64_decode( + const char* input, size_t input_len, uint8_t* output, size_t output_len) +{ + size_t i, j; + + if (input_len < 4 || + (input_len & 3) != 0 || + (input_len / 4) * 3 != output_len) + { + return EINVAL; + } + + for (i = 0, j = 0; i < input_len; i += 4, j += 3) + { + hdr_base64_decode_block(&input[i], &output[j]); + } + + return 0; +} diff --git a/src/hdr_encoding.h b/src/hdr_encoding.h new file mode 100644 index 0000000..95f1db6 --- /dev/null +++ b/src/hdr_encoding.h @@ -0,0 +1,79 @@ +/** + * hdr_encoding.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_ENCODING_H +#define HDR_ENCODING_H + +#include + +#define MAX_BYTES_LEB128 9 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Writes a int64_t value to the given buffer in LEB128 ZigZag encoded format + * + * @param buffer the buffer to write to + * @param signed_value the value to write to the buffer + * @return the number of bytes written to the buffer + */ +int zig_zag_encode_i64(uint8_t* buffer, int64_t signed_value); + +/** + * Read an LEB128 ZigZag encoded long value from the given buffer + * + * @param buffer the buffer to read from + * @param retVal out value to capture the read value + * @return the number of bytes read from the buffer + */ +int zig_zag_decode_i64(const uint8_t* buffer, int64_t* signed_value); + +/** + * Gets the length in bytes of base64 data, given the input size. + * + * @param decoded_size the size of the unencoded values. + * @return the encoded size + */ +size_t hdr_base64_encoded_len(size_t decoded_size); + +/** + * Encode into base64. + * + * @param input the data to encode + * @param input_len the length of the data to encode + * @param output the buffer to write the output to + * @param output_len the number of bytes to write to the output + */ +int hdr_base64_encode( + const uint8_t* input, size_t input_len, char* output, size_t output_len); + +/** + * Gets the length in bytes of decoded base64 data, given the size of the base64 encoded + * data. + * + * @param encoded_size the size of the encoded value. + * @return the decoded size + */ +size_t hdr_base64_decoded_len(size_t encoded_size); + +/** + * Decode from base64. + * + * @param input the base64 encoded data + * @param input_len the size in bytes of the endcoded data + * @param output the buffer to write the decoded data to + * @param output_len the number of bytes to write to the output data + */ +int hdr_base64_decode( + const char* input, size_t input_len, uint8_t* output, size_t output_len); + +#ifdef __cplusplus +} +#endif + +#endif /* HDR_HISTOGRAM_HDR_ENCODING_H */ diff --git a/src/hdr_endian.h b/src/hdr_endian.h new file mode 100644 index 0000000..839fdb1 --- /dev/null +++ b/src/hdr_endian.h @@ -0,0 +1,116 @@ +/** +* hdr_time.h +* Released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#ifndef HDR_ENDIAN_H__ +#define HDR_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif diff --git a/src/hdr_histogram.c b/src/hdr_histogram.c new file mode 100644 index 0000000..c33cc92 --- /dev/null +++ b/src/hdr_histogram.c @@ -0,0 +1,1155 @@ +/** + * hdr_histogram.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hdr_histogram.h" +#include "hdr_tests.h" +#include "hdr_atomic.h" + +/* ###### ####### ## ## ## ## ######## ###### */ +/* ## ## ## ## ## ## ### ## ## ## ## */ +/* ## ## ## ## ## #### ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ###### */ +/* ## ## ## ## ## ## #### ## ## */ +/* ## ## ## ## ## ## ## ### ## ## ## */ +/* ###### ####### ####### ## ## ## ###### */ + +static int32_t normalize_index(const struct hdr_histogram* h, int32_t index) +{ + int32_t normalized_index; + int32_t adjustment = 0; + if (h->normalizing_index_offset == 0) + { + return index; + } + + normalized_index = index - h->normalizing_index_offset; + + if (normalized_index < 0) + { + adjustment = h->counts_len; + } + else if (normalized_index >= h->counts_len) + { + adjustment = -h->counts_len; + } + + return normalized_index + adjustment; +} + +static int64_t counts_get_direct(const struct hdr_histogram* h, int32_t index) +{ + return h->counts[index]; +} + +static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t index) +{ + return counts_get_direct(h, normalize_index(h, index)); +} + +static void counts_inc_normalised( + struct hdr_histogram* h, int32_t index, int64_t value) +{ + int32_t normalised_index = normalize_index(h, index); + h->counts[normalised_index] += value; + h->total_count += value; +} + +static void counts_inc_normalised_atomic( + struct hdr_histogram* h, int32_t index, int64_t value) +{ + int32_t normalised_index = normalize_index(h, index); + + hdr_atomic_add_fetch_64(&h->counts[normalised_index], value); + hdr_atomic_add_fetch_64(&h->total_count, value); +} + +static void update_min_max(struct hdr_histogram* h, int64_t value) +{ + h->min_value = (value < h->min_value && value != 0) ? value : h->min_value; + h->max_value = (value > h->max_value) ? value : h->max_value; +} + +static void update_min_max_atomic(struct hdr_histogram* h, int64_t value) +{ + int64_t current_min_value; + int64_t current_max_value; + do + { + current_min_value = hdr_atomic_load_64(&h->min_value); + + if (0 == value || current_min_value <= value) + { + break; + } + } + while (!hdr_atomic_compare_exchange_64(&h->min_value, ¤t_min_value, value)); + + do + { + current_max_value = hdr_atomic_load_64(&h->max_value); + + if (value <= current_max_value) + { + break; + } + } + while (!hdr_atomic_compare_exchange_64(&h->max_value, ¤t_max_value, value)); +} + + +/* ## ## ######## #### ## #### ######## ## ## */ +/* ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## #### */ +/* ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## */ +/* ####### ## #### ######## #### ## ## */ + +static int64_t power(int64_t base, int64_t exp) +{ + int64_t result = 1; + while(exp) + { + result *= base; exp--; + } + return result; +} + +#if defined(_MSC_VER) +# if defined(_WIN64) +# pragma intrinsic(_BitScanReverse64) +# else +# pragma intrinsic(_BitScanReverse) +# endif +#endif + +static int32_t count_leading_zeros_64(int64_t value) +{ +#if defined(_MSC_VER) + uint32_t leading_zero = 0; +#if defined(_WIN64) + _BitScanReverse64(&leading_zero, value); +#else + uint32_t high = value >> 32; + if (_BitScanReverse(&leading_zero, high)) + { + leading_zero += 32; + } + else + { + uint32_t low = value & 0x00000000FFFFFFFF; + _BitScanReverse(&leading_zero, low); + } +#endif + return 63 - leading_zero; /* smallest power of 2 containing value */ +#else + return __builtin_clzll(value); /* smallest power of 2 containing value */ +#endif +} + +static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value) +{ + int32_t pow2ceiling = 64 - count_leading_zeros_64(value | h->sub_bucket_mask); /* smallest power of 2 containing value */ + return pow2ceiling - h->unit_magnitude - (h->sub_bucket_half_count_magnitude + 1); +} + +static int32_t get_sub_bucket_index(int64_t value, int32_t bucket_index, int32_t unit_magnitude) +{ + return (int32_t)(value >> (bucket_index + unit_magnitude)); +} + +static int32_t counts_index(const struct hdr_histogram* h, int32_t bucket_index, int32_t sub_bucket_index) +{ + /* Calculate the index for the first entry in the bucket: */ + /* (The following is the equivalent of ((bucket_index + 1) * subBucketHalfCount) ): */ + int32_t bucket_base_index = (bucket_index + 1) << h->sub_bucket_half_count_magnitude; + /* Calculate the offset in the bucket: */ + int32_t offset_in_bucket = sub_bucket_index - h->sub_bucket_half_count; + /* The following is the equivalent of ((sub_bucket_index - subBucketHalfCount) + bucketBaseIndex; */ + return bucket_base_index + offset_in_bucket; +} + +static int64_t value_from_index(int32_t bucket_index, int32_t sub_bucket_index, int32_t unit_magnitude) +{ + return ((int64_t) sub_bucket_index) << (bucket_index + unit_magnitude); +} + +int32_t counts_index_for(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + + return counts_index(h, bucket_index, sub_bucket_index); +} + +int64_t hdr_value_at_index(const struct hdr_histogram *h, int32_t index) +{ + int32_t bucket_index = (index >> h->sub_bucket_half_count_magnitude) - 1; + int32_t sub_bucket_index = (index & (h->sub_bucket_half_count - 1)) + h->sub_bucket_half_count; + + if (bucket_index < 0) + { + sub_bucket_index -= h->sub_bucket_half_count; + bucket_index = 0; + } + + return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); +} + +int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + int32_t adjusted_bucket = (sub_bucket_index >= h->sub_bucket_count) ? (bucket_index + 1) : bucket_index; + return INT64_C(1) << (h->unit_magnitude + adjusted_bucket); +} + +static int64_t lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); +} + +int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value) +{ + return lowest_equivalent_value(h, value) + hdr_size_of_equivalent_value_range(h, value); +} + +static int64_t highest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + return hdr_next_non_equivalent_value(h, value) - 1; +} + +int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value) +{ + return lowest_equivalent_value(h, value) + (hdr_size_of_equivalent_value_range(h, value) >> 1); +} + +static int64_t non_zero_min(const struct hdr_histogram* h) +{ + if (INT64_MAX == h->min_value) + { + return INT64_MAX; + } + + return lowest_equivalent_value(h, h->min_value); +} + +void hdr_reset_internal_counters(struct hdr_histogram* h) +{ + int min_non_zero_index = -1; + int max_index = -1; + int64_t observed_total_count = 0; + int i; + + for (i = 0; i < h->counts_len; i++) + { + int64_t count_at_index; + + if ((count_at_index = counts_get_direct(h, i)) > 0) + { + observed_total_count += count_at_index; + max_index = i; + if (min_non_zero_index == -1 && i != 0) + { + min_non_zero_index = i; + } + } + } + + if (max_index == -1) + { + h->max_value = 0; + } + else + { + int64_t max_value = hdr_value_at_index(h, max_index); + h->max_value = highest_equivalent_value(h, max_value); + } + + if (min_non_zero_index == -1) + { + h->min_value = INT64_MAX; + } + else + { + h->min_value = hdr_value_at_index(h, min_non_zero_index); + } + + h->total_count = observed_total_count; +} + +static int32_t buckets_needed_to_cover_value(int64_t value, int32_t sub_bucket_count, int32_t unit_magnitude) +{ + int64_t smallest_untrackable_value = ((int64_t) sub_bucket_count) << unit_magnitude; + int32_t buckets_needed = 1; + while (smallest_untrackable_value <= value) + { + if (smallest_untrackable_value > INT64_MAX / 2) + { + return buckets_needed + 1; + } + smallest_untrackable_value <<= 1; + buckets_needed++; + } + + return buckets_needed; +} + +/* ## ## ######## ## ## ####### ######## ## ## */ +/* ### ### ## ### ### ## ## ## ## ## ## */ +/* #### #### ## #### #### ## ## ## ## #### */ +/* ## ### ## ###### ## ### ## ## ## ######## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ######## ## ## ####### ## ## ## */ + +int hdr_calculate_bucket_config( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram_bucket_config* cfg) +{ + int32_t sub_bucket_count_magnitude; + int64_t largest_value_with_single_unit_resolution; + + if (lowest_trackable_value < 1 || + significant_figures < 1 || 5 < significant_figures) + { + return EINVAL; + } + else if (lowest_trackable_value * 2 > highest_trackable_value) + { + return EINVAL; + } + + cfg->lowest_trackable_value = lowest_trackable_value; + cfg->significant_figures = significant_figures; + cfg->highest_trackable_value = highest_trackable_value; + + largest_value_with_single_unit_resolution = 2 * power(10, significant_figures); + sub_bucket_count_magnitude = (int32_t) ceil(log((double)largest_value_with_single_unit_resolution) / log(2)); + cfg->sub_bucket_half_count_magnitude = ((sub_bucket_count_magnitude > 1) ? sub_bucket_count_magnitude : 1) - 1; + + cfg->unit_magnitude = (int32_t) floor(log((double)lowest_trackable_value) / log(2)); + + cfg->sub_bucket_count = (int32_t) pow(2, (cfg->sub_bucket_half_count_magnitude + 1)); + cfg->sub_bucket_half_count = cfg->sub_bucket_count / 2; + cfg->sub_bucket_mask = ((int64_t) cfg->sub_bucket_count - 1) << cfg->unit_magnitude; + + if (cfg->unit_magnitude + cfg->sub_bucket_half_count_magnitude > 61) + { + return EINVAL; + } + + cfg->bucket_count = buckets_needed_to_cover_value(highest_trackable_value, cfg->sub_bucket_count, (int32_t)cfg->unit_magnitude); + cfg->counts_len = (cfg->bucket_count + 1) * (cfg->sub_bucket_count / 2); + + return 0; +} + +void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg) +{ + h->lowest_trackable_value = cfg->lowest_trackable_value; + h->highest_trackable_value = cfg->highest_trackable_value; + h->unit_magnitude = (int32_t)cfg->unit_magnitude; + h->significant_figures = (int32_t)cfg->significant_figures; + h->sub_bucket_half_count_magnitude = cfg->sub_bucket_half_count_magnitude; + h->sub_bucket_half_count = cfg->sub_bucket_half_count; + h->sub_bucket_mask = cfg->sub_bucket_mask; + h->sub_bucket_count = cfg->sub_bucket_count; + h->min_value = INT64_MAX; + h->max_value = 0; + h->normalizing_index_offset = 0; + h->conversion_ratio = 1.0; + h->bucket_count = cfg->bucket_count; + h->counts_len = cfg->counts_len; + h->total_count = 0; +} + +int hdr_init( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram** result) +{ + int64_t* counts; + struct hdr_histogram_bucket_config cfg; + struct hdr_histogram* histogram; + + int r = hdr_calculate_bucket_config(lowest_trackable_value, highest_trackable_value, significant_figures, &cfg); + if (r) + { + return r; + } + + counts = (int64_t*) calloc((size_t) cfg.counts_len, sizeof(int64_t)); + if (!counts) + { + return ENOMEM; + } + + histogram = (struct hdr_histogram*) calloc(1, sizeof(struct hdr_histogram)); + if (!histogram) + { + return ENOMEM; + } + + histogram->counts = counts; + + hdr_init_preallocated(histogram, &cfg); + *result = histogram; + + return 0; +} + +void hdr_close(struct hdr_histogram* h) +{ + if (h) { + free(h->counts); + free(h); + } +} + +int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result) +{ + return hdr_init(1, highest_trackable_value, significant_figures, result); +} + +/* reset a histogram to zero. */ +void hdr_reset(struct hdr_histogram *h) +{ + h->total_count=0; + h->min_value = INT64_MAX; + h->max_value = 0; + memset(h->counts, 0, (sizeof(int64_t) * h->counts_len)); +} + +size_t hdr_get_memory_size(struct hdr_histogram *h) +{ + return sizeof(struct hdr_histogram) + h->counts_len * sizeof(int64_t); +} + +/* ## ## ######## ######## ### ######## ######## ###### */ +/* ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ######## ## ## ## ## ## ###### ###### */ +/* ## ## ## ## ## ######### ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## */ +/* ####### ## ######## ## ## ## ######## ###### */ + + +bool hdr_record_value(struct hdr_histogram* h, int64_t value) +{ + return hdr_record_values(h, value, 1); +} + +bool hdr_record_value_atomic(struct hdr_histogram* h, int64_t value) +{ + return hdr_record_values_atomic(h, value, 1); +} + +bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count) +{ + int32_t counts_index; + + if (value < 0) + { + return false; + } + + counts_index = counts_index_for(h, value); + + if (counts_index < 0 || h->counts_len <= counts_index) + { + return false; + } + + counts_inc_normalised(h, counts_index, count); + update_min_max(h, value); + + return true; +} + +bool hdr_record_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count) +{ + int32_t counts_index; + + if (value < 0) + { + return false; + } + + counts_index = counts_index_for(h, value); + + if (counts_index < 0 || h->counts_len <= counts_index) + { + return false; + } + + counts_inc_normalised_atomic(h, counts_index, count); + update_min_max_atomic(h, value); + + return true; +} + +bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expected_interval) +{ + return hdr_record_corrected_values(h, value, 1, expected_interval); +} + +bool hdr_record_corrected_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expected_interval) +{ + return hdr_record_corrected_values_atomic(h, value, 1, expected_interval); +} + +bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) +{ + int64_t missing_value; + + if (!hdr_record_values(h, value, count)) + { + return false; + } + + if (expected_interval <= 0 || value <= expected_interval) + { + return true; + } + + missing_value = value - expected_interval; + for (; missing_value >= expected_interval; missing_value -= expected_interval) + { + if (!hdr_record_values(h, missing_value, count)) + { + return false; + } + } + + return true; +} + +bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) +{ + int64_t missing_value; + + if (!hdr_record_values_atomic(h, value, count)) + { + return false; + } + + if (expected_interval <= 0 || value <= expected_interval) + { + return true; + } + + missing_value = value - expected_interval; + for (; missing_value >= expected_interval; missing_value -= expected_interval) + { + if (!hdr_record_values_atomic(h, missing_value, count)) + { + return false; + } + } + + return true; +} + +int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from) +{ + struct hdr_iter iter; + int64_t dropped = 0; + hdr_iter_recorded_init(&iter, from); + + while (hdr_iter_next(&iter)) + { + int64_t value = iter.value; + int64_t count = iter.count; + + if (!hdr_record_values(h, value, count)) + { + dropped += count; + } + } + + return dropped; +} + +int64_t hdr_add_while_correcting_for_coordinated_omission( + struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval) +{ + struct hdr_iter iter; + int64_t dropped = 0; + hdr_iter_recorded_init(&iter, from); + + while (hdr_iter_next(&iter)) + { + int64_t value = iter.value; + int64_t count = iter.count; + + if (!hdr_record_corrected_values(h, value, count, expected_interval)) + { + dropped += count; + } + } + + return dropped; +} + + + +/* ## ## ### ## ## ## ######## ###### */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ###### ###### */ +/* ## ## ######### ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ### ## ## ######## ####### ######## ###### */ + + +int64_t hdr_max(const struct hdr_histogram* h) +{ + if (0 == h->max_value) + { + return 0; + } + + return highest_equivalent_value(h, h->max_value); +} + +int64_t hdr_min(const struct hdr_histogram* h) +{ + if (0 < hdr_count_at_index(h, 0)) + { + return 0; + } + + return non_zero_min(h); +} + +int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile) +{ + struct hdr_iter iter; + int64_t total = 0; + double requested_percentile = percentile < 100.0 ? percentile : 100.0; + int64_t count_at_percentile = + (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5); + count_at_percentile = count_at_percentile > 1 ? count_at_percentile : 1; + + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter)) + { + total += iter.count; + + if (total >= count_at_percentile) + { + int64_t value_from_index = iter.value; + return highest_equivalent_value(h, value_from_index); + } + } + + return 0; +} + +double hdr_mean(const struct hdr_histogram* h) +{ + struct hdr_iter iter; + int64_t total = 0; + + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter)) + { + if (0 != iter.count) + { + total += iter.count * hdr_median_equivalent_value(h, iter.value); + } + } + + return (total * 1.0) / h->total_count; +} + +double hdr_stddev(const struct hdr_histogram* h) +{ + double mean = hdr_mean(h); + double geometric_dev_total = 0.0; + + struct hdr_iter iter; + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter)) + { + if (0 != iter.count) + { + double dev = (hdr_median_equivalent_value(h, iter.value) * 1.0) - mean; + geometric_dev_total += (dev * dev) * iter.count; + } + } + + return sqrt(geometric_dev_total / h->total_count); +} + +bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b) +{ + return lowest_equivalent_value(h, a) == lowest_equivalent_value(h, b); +} + +int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + return lowest_equivalent_value(h, value); +} + +int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value) +{ + return counts_get_normalised(h, counts_index_for(h, value)); +} + +int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index) +{ + return counts_get_normalised(h, index); +} + + +/* #### ######## ######## ######## ### ######## ####### ######## ###### */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ###### ######## ## ## ## ## ## ######## ###### */ +/* ## ## ## ## ## ######### ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* #### ## ######## ## ## ## ## ## ####### ## ## ###### */ + + +static bool has_buckets(struct hdr_iter* iter) +{ + return iter->counts_index < iter->h->counts_len; +} + +static bool has_next(struct hdr_iter* iter) +{ + return iter->cumulative_count < iter->total_count; +} + +static bool move_next(struct hdr_iter* iter) +{ + iter->counts_index++; + + if (!has_buckets(iter)) + { + return false; + } + + iter->count = counts_get_normalised(iter->h, iter->counts_index); + iter->cumulative_count += iter->count; + + iter->value = hdr_value_at_index(iter->h, iter->counts_index); + iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value); + iter->lowest_equivalent_value = lowest_equivalent_value(iter->h, iter->value); + iter->median_equivalent_value = hdr_median_equivalent_value(iter->h, iter->value); + + return true; +} + +static int64_t peek_next_value_from_index(struct hdr_iter* iter) +{ + return hdr_value_at_index(iter->h, iter->counts_index + 1); +} + +static bool next_value_greater_than_reporting_level_upper_bound( + struct hdr_iter *iter, int64_t reporting_level_upper_bound) +{ + if (iter->counts_index >= iter->h->counts_len) + { + return false; + } + + return peek_next_value_from_index(iter) > reporting_level_upper_bound; +} + +static bool _basic_iter_next(struct hdr_iter *iter) +{ + if (!has_next(iter) || iter->counts_index >= iter->h->counts_len) + { + return false; + } + + move_next(iter); + + return true; +} + +static void _update_iterated_values(struct hdr_iter* iter, int64_t new_value_iterated_to) +{ + iter->value_iterated_from = iter->value_iterated_to; + iter->value_iterated_to = new_value_iterated_to; +} + +static bool _all_values_iter_next(struct hdr_iter* iter) +{ + bool result = move_next(iter); + + if (result) + { + _update_iterated_values(iter, iter->value); + } + + return result; +} + +void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h) +{ + iter->h = h; + + iter->counts_index = -1; + iter->total_count = h->total_count; + iter->count = 0; + iter->cumulative_count = 0; + iter->value = 0; + iter->highest_equivalent_value = 0; + iter->value_iterated_from = 0; + iter->value_iterated_to = 0; + + iter->_next_fp = _all_values_iter_next; +} + +bool hdr_iter_next(struct hdr_iter* iter) +{ + return iter->_next_fp(iter); +} + +/* ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ###### */ +/* ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## #### ## ## ## ## ## ## */ +/* ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ###### */ +/* ## ## ## ## ## ## ## #### ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## */ +/* ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ###### */ + +static bool _percentile_iter_next(struct hdr_iter* iter) +{ + int64_t temp, half_distance, percentile_reporting_ticks; + + struct hdr_iter_percentiles* percentiles = &iter->specifics.percentiles; + + if (!has_next(iter)) + { + if (percentiles->seen_last_value) + { + return false; + } + + percentiles->seen_last_value = true; + percentiles->percentile = 100.0; + + return true; + } + + if (iter->counts_index == -1 && !_basic_iter_next(iter)) + { + return false; + } + + do + { + double current_percentile = (100.0 * (double) iter->cumulative_count) / iter->h->total_count; + if (iter->count != 0 && + percentiles->percentile_to_iterate_to <= current_percentile) + { + _update_iterated_values(iter, highest_equivalent_value(iter->h, iter->value)); + + percentiles->percentile = percentiles->percentile_to_iterate_to; + temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1; + half_distance = (int64_t) pow(2, (double) temp); + percentile_reporting_ticks = percentiles->ticks_per_half_distance * half_distance; + percentiles->percentile_to_iterate_to += 100.0 / percentile_reporting_ticks; + + return true; + } + } + while (_basic_iter_next(iter)); + + return true; +} + +void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance) +{ + iter->h = h; + + hdr_iter_init(iter, h); + + iter->specifics.percentiles.seen_last_value = false; + iter->specifics.percentiles.ticks_per_half_distance = ticks_per_half_distance; + iter->specifics.percentiles.percentile_to_iterate_to = 0.0; + iter->specifics.percentiles.percentile = 0.0; + + iter->_next_fp = _percentile_iter_next; +} + +static void format_line_string(char* str, size_t len, int significant_figures, format_type format) +{ +#if defined(_MSC_VER) +#define snprintf _snprintf +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + const char* format_str = "%s%d%s"; + + switch (format) + { + case CSV: + snprintf(str, len, format_str, "%.", significant_figures, "f,%f,%d,%.2f\n"); + break; + case CLASSIC: + snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); + break; + default: + snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); + } +#if defined(_MSC_VER) +#undef snprintf +#pragma warning(pop) +#endif +} + + +/* ######## ######## ###### ####### ######## ######## ######## ######## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ######## ###### ## ## ## ######## ## ## ###### ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ######## ###### ####### ## ## ######## ######## ######## */ + + +static bool _recorded_iter_next(struct hdr_iter* iter) +{ + while (_basic_iter_next(iter)) + { + if (iter->count != 0) + { + _update_iterated_values(iter, iter->value); + + iter->specifics.recorded.count_added_in_this_iteration_step = iter->count; + return true; + } + } + + return false; +} + +void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h) +{ + hdr_iter_init(iter, h); + + iter->specifics.recorded.count_added_in_this_iteration_step = 0; + + iter->_next_fp = _recorded_iter_next; +} + +/* ## #### ## ## ######## ### ######## */ +/* ## ## ### ## ## ## ## ## ## */ +/* ## ## #### ## ## ## ## ## ## */ +/* ## ## ## ## ## ###### ## ## ######## */ +/* ## ## ## #### ## ######### ## ## */ +/* ## ## ## ### ## ## ## ## ## */ +/* ######## #### ## ## ######## ## ## ## ## */ + + +static bool _iter_linear_next(struct hdr_iter* iter) +{ + struct hdr_iter_linear* linear = &iter->specifics.linear; + + linear->count_added_in_this_iteration_step = 0; + + if (has_next(iter) || + next_value_greater_than_reporting_level_upper_bound( + iter, linear->next_value_reporting_level_lowest_equivalent)) + { + do + { + if (iter->value >= linear->next_value_reporting_level_lowest_equivalent) + { + _update_iterated_values(iter, linear->next_value_reporting_level); + + linear->next_value_reporting_level += linear->value_units_per_bucket; + linear->next_value_reporting_level_lowest_equivalent = + lowest_equivalent_value(iter->h, linear->next_value_reporting_level); + + return true; + } + + if (!move_next(iter)) + { + return true; + } + + linear->count_added_in_this_iteration_step += iter->count; + } + while (true); + } + + return false; +} + + +void hdr_iter_linear_init(struct hdr_iter* iter, const struct hdr_histogram* h, int64_t value_units_per_bucket) +{ + hdr_iter_init(iter, h); + + iter->specifics.linear.count_added_in_this_iteration_step = 0; + iter->specifics.linear.value_units_per_bucket = value_units_per_bucket; + iter->specifics.linear.next_value_reporting_level = value_units_per_bucket; + iter->specifics.linear.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_per_bucket); + + iter->_next_fp = _iter_linear_next; +} + +/* ## ####### ###### ### ######## #### ######## ## ## ## ## #### ###### */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## */ +/* ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ## */ +/* ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ +/* ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ###### */ + +static bool _log_iter_next(struct hdr_iter *iter) +{ + struct hdr_iter_log* logarithmic = &iter->specifics.log; + + logarithmic->count_added_in_this_iteration_step = 0; + + if (has_next(iter) || + next_value_greater_than_reporting_level_upper_bound( + iter, logarithmic->next_value_reporting_level_lowest_equivalent)) + { + do + { + if (iter->value >= logarithmic->next_value_reporting_level_lowest_equivalent) + { + _update_iterated_values(iter, logarithmic->next_value_reporting_level); + + logarithmic->next_value_reporting_level *= (int64_t)logarithmic->log_base; + logarithmic->next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(iter->h, logarithmic->next_value_reporting_level); + + return true; + } + + if (!move_next(iter)) + { + return true; + } + + logarithmic->count_added_in_this_iteration_step += iter->count; + } + while (true); + } + + return false; +} + +void hdr_iter_log_init( + struct hdr_iter* iter, + const struct hdr_histogram* h, + int64_t value_units_first_bucket, + double log_base) +{ + hdr_iter_init(iter, h); + iter->specifics.log.count_added_in_this_iteration_step = 0; + iter->specifics.log.log_base = log_base; + iter->specifics.log.next_value_reporting_level = value_units_first_bucket; + iter->specifics.log.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_first_bucket); + + iter->_next_fp = _log_iter_next; +} + +/* Printing. */ + +static const char* format_head_string(format_type format) +{ + switch (format) + { + case CSV: + return "%s,%s,%s,%s\n"; + case CLASSIC: + return "%12s %12s %12s %12s\n\n"; + default: + return "%12s %12s %12s %12s\n\n"; + } +} + +static const char CLASSIC_FOOTER[] = + "#[Mean = %12.3f, StdDeviation = %12.3f]\n" + "#[Max = %12.3f, Total count = %12" PRIu64 "]\n" + "#[Buckets = %12d, SubBuckets = %12d]\n"; + +int hdr_percentiles_print( + struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance, + double value_scale, format_type format) +{ + char line_format[25]; + const char* head_format; + int rc = 0; + struct hdr_iter iter; + struct hdr_iter_percentiles * percentiles; + + format_line_string(line_format, 25, h->significant_figures, format); + head_format = format_head_string(format); + + hdr_iter_percentile_init(&iter, h, ticks_per_half_distance); + + if (fprintf( + stream, head_format, + "Value", "Percentile", "TotalCount", "1/(1-Percentile)") < 0) + { + rc = EIO; + goto cleanup; + } + + percentiles = &iter.specifics.percentiles; + while (hdr_iter_next(&iter)) + { + double value = iter.highest_equivalent_value / value_scale; + double percentile = percentiles->percentile / 100.0; + int64_t total_count = iter.cumulative_count; + double inverted_percentile = (1.0 / (1.0 - percentile)); + + if (fprintf( + stream, line_format, value, percentile, total_count, inverted_percentile) < 0) + { + rc = EIO; + goto cleanup; + } + } + + if (CLASSIC == format) + { + double mean = hdr_mean(h) / value_scale; + double stddev = hdr_stddev(h) / value_scale; + double max = hdr_max(h) / value_scale; + + if (fprintf( + stream, CLASSIC_FOOTER, mean, stddev, max, + h->total_count, h->bucket_count, h->sub_bucket_count) < 0) + { + rc = EIO; + goto cleanup; + } + } + + cleanup: + return rc; +} diff --git a/src/hdr_histogram.h b/src/hdr_histogram.h new file mode 100644 index 0000000..aff51f1 --- /dev/null +++ b/src/hdr_histogram.h @@ -0,0 +1,504 @@ +/** + * hdr_histogram.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + * + * The source for the hdr_histogram utilises a few C99 constructs, specifically + * the use of stdint/stdbool and inline variable declaration. + */ + +#ifndef HDR_HISTOGRAM_H +#define HDR_HISTOGRAM_H 1 + +#include +#include +#include + +struct hdr_histogram +{ + int64_t lowest_trackable_value; + int64_t highest_trackable_value; + int32_t unit_magnitude; + int32_t significant_figures; + int32_t sub_bucket_half_count_magnitude; + int32_t sub_bucket_half_count; + int64_t sub_bucket_mask; + int32_t sub_bucket_count; + int32_t bucket_count; + int64_t min_value; + int64_t max_value; + int32_t normalizing_index_offset; + double conversion_ratio; + int32_t counts_len; + int64_t total_count; + int64_t* counts; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocate the memory and initialise the hdr_histogram. + * + * Due to the size of the histogram being the result of some reasonably + * involved math on the input parameters this function it is tricky to stack allocate. + * The histogram should be released with hdr_close + * + * @param lowest_trackable_value The smallest possible value to be put into the + * histogram. + * @param highest_trackable_value The largest possible value to be put into the + * histogram. + * @param significant_figures The level of precision for this histogram, i.e. the number + * of figures in a decimal number that will be maintained. E.g. a value of 3 will mean + * the results from the histogram will be accurate up to the first three digits. Must + * be a value between 1 and 5 (inclusive). + * @param result Output parameter to capture allocated histogram. + * @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the + * significant_figure value is outside of the allowed range, ENOMEM if malloc + * failed. + */ +int hdr_init( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram** result); + +/** + * Free the memory and close the hdr_histogram. + * + * @param h The histogram you want to close. + */ +void hdr_close(struct hdr_histogram* h); + +/** + * Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling + * hdr_init(1, highest_trackable_value, significant_figures, result); + * + * @deprecated use hdr_init. + */ +int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result); + + +/** + * Reset a histogram to zero - empty out a histogram and re-initialise it + * + * If you want to re-use an existing histogram, but reset everything back to zero, this + * is the routine to use. + * + * @param h The histogram you want to reset to empty. + * + */ +void hdr_reset(struct hdr_histogram* h); + +/** + * Get the memory size of the hdr_histogram. + * + * @param h "This" pointer + * @return The amount of memory used by the hdr_histogram in bytes + */ +size_t hdr_get_memory_size(struct hdr_histogram* h); + +/** + * Records a value in the histogram, will round this value of to a precision at or better + * than the significant_figure specified at construction time. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_value(struct hdr_histogram* h, int64_t value); + +/** + * Records a value in the histogram, will round this value of to a precision at or better + * than the significant_figure specified at construction time. + * + * Will record this value atomically, however the whole structure may appear inconsistent + * when read concurrently with this update. Do NOT mix calls to this method with calls + * to non-atomic updates. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_value_atomic(struct hdr_histogram* h, int64_t value); + +/** + * Records count values in the histogram, will round this value of to a + * precision at or better than the significant_figure specified at construction + * time. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @param count Number of 'value's to add to the histogram + * @return false if any value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count); + +/** + * Records count values in the histogram, will round this value of to a + * precision at or better than the significant_figure specified at construction + * time. + * + * Will record this value atomically, however the whole structure may appear inconsistent + * when read concurrently with this update. Do NOT mix calls to this method with calls + * to non-atomic updates. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @param count Number of 'value's to add to the histogram + * @return false if any value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count); + +/** + * Record a value in the histogram and backfill based on an expected interval. + * + * Records a value in the histogram, will round this value of to a precision at or better + * than the significant_figure specified at contruction time. This is specifically used + * for recording latency. If the value is larger than the expected_interval then the + * latency recording system has experienced co-ordinated omission. This method fills in the + * values that would have occured had the client providing the load not been blocked. + + * @param h "This" pointer + * @param value Value to add to the histogram + * @param expected_interval The delay between recording values. + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval); + +/** + * Record a value in the histogram and backfill based on an expected interval. + * + * Records a value in the histogram, will round this value of to a precision at or better + * than the significant_figure specified at contruction time. This is specifically used + * for recording latency. If the value is larger than the expected_interval then the + * latency recording system has experienced co-ordinated omission. This method fills in the + * values that would have occured had the client providing the load not been blocked. + * + * Will record this value atomically, however the whole structure may appear inconsistent + * when read concurrently with this update. Do NOT mix calls to this method with calls + * to non-atomic updates. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @param expected_interval The delay between recording values. + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_corrected_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval); + +/** + * Record a value in the histogram 'count' times. Applies the same correcting logic + * as 'hdr_record_corrected_value'. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @param count Number of 'value's to add to the histogram + * @param expected_interval The delay between recording values. + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval); + +/** + * Record a value in the histogram 'count' times. Applies the same correcting logic + * as 'hdr_record_corrected_value'. + * + * Will record this value atomically, however the whole structure may appear inconsistent + * when read concurrently with this update. Do NOT mix calls to this method with calls + * to non-atomic updates. + * + * @param h "This" pointer + * @param value Value to add to the histogram + * @param count Number of 'value's to add to the histogram + * @param expected_interval The delay between recording values. + * @return false if the value is larger than the highest_trackable_value and can't be recorded, + * true otherwise. + */ +bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval); + +/** + * Adds all of the values from 'from' to 'this' histogram. Will return the + * number of values that are dropped when copying. Values will be dropped + * if they around outside of h.lowest_trackable_value and + * h.highest_trackable_value. + * + * @param h "This" pointer + * @param from Histogram to copy values from. + * @return The number of values dropped when copying. + */ +int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from); + +/** + * Adds all of the values from 'from' to 'this' histogram. Will return the + * number of values that are dropped when copying. Values will be dropped + * if they around outside of h.lowest_trackable_value and + * h.highest_trackable_value. + * + * @param h "This" pointer + * @param from Histogram to copy values from. + * @return The number of values dropped when copying. + */ +int64_t hdr_add_while_correcting_for_coordinated_omission( + struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval); + +/** + * Get minimum value from the histogram. Will return 2^63-1 if the histogram + * is empty. + * + * @param h "This" pointer + */ +int64_t hdr_min(const struct hdr_histogram* h); + +/** + * Get maximum value from the histogram. Will return 0 if the histogram + * is empty. + * + * @param h "This" pointer + */ +int64_t hdr_max(const struct hdr_histogram* h); + +/** + * Get the value at a specific percentile. + * + * @param h "This" pointer. + * @param percentile The percentile to get the value for + */ +int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile); + +/** + * Gets the standard deviation for the values in the histogram. + * + * @param h "This" pointer + * @return The standard deviation + */ +double hdr_stddev(const struct hdr_histogram* h); + +/** + * Gets the mean for the values in the histogram. + * + * @param h "This" pointer + * @return The mean + */ +double hdr_mean(const struct hdr_histogram* h); + +/** + * Determine if two values are equivalent with the histogram's resolution. + * Where "equivalent" means that value samples recorded for any two + * equivalent values are counted in a common total count. + * + * @param h "This" pointer + * @param a first value to compare + * @param b second value to compare + * @return 'true' if values are equivalent with the histogram's resolution. + */ +bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b); + +/** + * Get the lowest value that is equivalent to the given value within the histogram's resolution. + * Where "equivalent" means that value samples recorded for any two + * equivalent values are counted in a common total count. + * + * @param h "This" pointer + * @param value The given value + * @return The lowest value that is equivalent to the given value within the histogram's resolution. + */ +int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value); + +/** + * Get the count of recorded values at a specific value + * (to within the histogram resolution at the value level). + * + * @param h "This" pointer + * @param value The value for which to provide the recorded count + * @return The total count of values recorded in the histogram within the value range that is + * {@literal >=} lowestEquivalentValue(value) and {@literal <=} highestEquivalentValue(value) + */ +int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value); + +int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index); + +int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index); + +struct hdr_iter_percentiles +{ + bool seen_last_value; + int32_t ticks_per_half_distance; + double percentile_to_iterate_to; + double percentile; +}; + +struct hdr_iter_recorded +{ + int64_t count_added_in_this_iteration_step; +}; + +struct hdr_iter_linear +{ + int64_t value_units_per_bucket; + int64_t count_added_in_this_iteration_step; + int64_t next_value_reporting_level; + int64_t next_value_reporting_level_lowest_equivalent; +}; + +struct hdr_iter_log +{ + double log_base; + int64_t count_added_in_this_iteration_step; + int64_t next_value_reporting_level; + int64_t next_value_reporting_level_lowest_equivalent; +}; + +/** + * The basic iterator. This is a generic structure + * that supports all of the types of iteration. Use + * the appropriate initialiser to get the desired + * iteration. + * + * @ + */ +struct hdr_iter +{ + const struct hdr_histogram* h; + /** raw index into the counts array */ + int32_t counts_index; + /** snapshot of the length at the time the iterator is created */ + int64_t total_count; + /** value directly from array for the current counts_index */ + int64_t count; + /** sum of all of the counts up to and including the count at this index */ + int64_t cumulative_count; + /** The current value based on counts_index */ + int64_t value; + int64_t highest_equivalent_value; + int64_t lowest_equivalent_value; + int64_t median_equivalent_value; + int64_t value_iterated_from; + int64_t value_iterated_to; + + union + { + struct hdr_iter_percentiles percentiles; + struct hdr_iter_recorded recorded; + struct hdr_iter_linear linear; + struct hdr_iter_log log; + } specifics; + + bool (* _next_fp)(struct hdr_iter* iter); + +}; + +/** + * Initalises the basic iterator. + * + * @param itr 'This' pointer + * @param h The histogram to iterate over + */ +void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h); + +/** + * Initialise the iterator for use with percentiles. + */ +void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance); + +/** + * Initialise the iterator for use with recorded values. + */ +void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h); + +/** + * Initialise the iterator for use with linear values. + */ +void hdr_iter_linear_init( + struct hdr_iter* iter, + const struct hdr_histogram* h, + int64_t value_units_per_bucket); + +/** + * Initialise the iterator for use with logarithmic values + */ +void hdr_iter_log_init( + struct hdr_iter* iter, + const struct hdr_histogram* h, + int64_t value_units_first_bucket, + double log_base); + +/** + * Iterate to the next value for the iterator. If there are no more values + * available return faluse. + * + * @param itr 'This' pointer + * @return 'false' if there are no values remaining for this iterator. + */ +bool hdr_iter_next(struct hdr_iter* iter); + +typedef enum +{ + CLASSIC, + CSV +} format_type; + +/** + * Print out a percentile based histogram to the supplied stream. Note that + * this call will not flush the FILE, this is left up to the user. + * + * @param h 'This' pointer + * @param stream The FILE to write the output to + * @param ticks_per_half_distance The number of iteration steps per half-distance to 100% + * @param value_scale Scale the output values by this amount + * @param format_type Format to use, e.g. CSV. + * @return 0 on success, error code on failure. EIO if an error occurs writing + * the output. + */ +int hdr_percentiles_print( + struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance, + double value_scale, format_type format); + +/** +* Internal allocation methods, used by hdr_dbl_histogram. +*/ +struct hdr_histogram_bucket_config +{ + int64_t lowest_trackable_value; + int64_t highest_trackable_value; + int64_t unit_magnitude; + int64_t significant_figures; + int32_t sub_bucket_half_count_magnitude; + int32_t sub_bucket_half_count; + int64_t sub_bucket_mask; + int32_t sub_bucket_count; + int32_t bucket_count; + int32_t counts_len; +}; + +int hdr_calculate_bucket_config( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram_bucket_config* cfg); + +void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg); + +int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value); + +int64_t hdr_next_non_equivalent_value(const struct hdr_histogram* h, int64_t value); + +int64_t hdr_median_equivalent_value(const struct hdr_histogram* h, int64_t value); + +/** + * Used to reset counters after importing data manuallying into the histogram, used by the logging code + * and other custom serialisation tools. + */ +void hdr_reset_internal_counters(struct hdr_histogram* h); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hdr_histogram_log.c b/src/hdr_histogram_log.c new file mode 100644 index 0000000..bb13861 --- /dev/null +++ b/src/hdr_histogram_log.c @@ -0,0 +1,1218 @@ +/** + * hdr_histogram_log.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include +#include +#include +#if defined(_MSC_VER) +#undef HAVE_UNISTD_H +#endif +#include +#include +#include +#include +#include + +#include "hdr_encoding.h" +#include "hdr_histogram.h" +#include "hdr_histogram_log.h" +#include "hdr_tests.h" + +#if defined(_MSC_VER) +#include +typedef SSIZE_T ssize_t; +#pragma comment(lib, "ws2_32.lib") +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + +#include "hdr_endian.h" + +/* Private prototypes useful for the logger */ +int32_t counts_index_for(const struct hdr_histogram* h, int64_t value); + + +#define FAIL_AND_CLEANUP(label, error_name, error) \ + do \ + { \ + error_name = error; \ + goto label; \ + } \ + while (0) + +static int realloc_buffer( + void** buffer, size_t nmemb, ssize_t size) +{ + size_t len = nmemb * size; + if (NULL == *buffer) + { + *buffer = malloc(len); + } + else + { + *buffer = realloc(*buffer, len); + } + + if (NULL == *buffer) + { + return ENOMEM; + } + else + { + memset(*buffer, 0, len); + return 0; + } +} + +/* ###### ######## ######## #### ## ## ###### ###### */ +/* ## ## ## ## ## ## ### ## ## ## ## ## */ +/* ## ## ## ## ## #### ## ## ## */ +/* ###### ## ######## ## ## ## ## ## #### ###### */ +/* ## ## ## ## ## ## #### ## ## ## */ +/* ## ## ## ## ## ## ## ### ## ## ## ## */ +/* ###### ## ## ## #### ## ## ###### ###### */ + +static ssize_t null_trailing_whitespace(char* s, ssize_t len) +{ + ssize_t i = len; + while (--i != -1) + { + if (isspace(s[i])) + { + s[i] = '\0'; + } + else + { + return i + 1; + } + } + + return 0; +} + +/* ######## ## ## ###### ####### ######## #### ## ## ###### */ +/* ## ### ## ## ## ## ## ## ## ## ### ## ## ## */ +/* ## #### ## ## ## ## ## ## ## #### ## ## */ +/* ###### ## ## ## ## ## ## ## ## ## ## ## ## ## #### */ +/* ## ## #### ## ## ## ## ## ## ## #### ## ## */ +/* ## ## ### ## ## ## ## ## ## ## ## ### ## ## */ +/* ######## ## ## ###### ####### ######## #### ## ## ###### */ + +static const int32_t V0_ENCODING_COOKIE = 0x1c849308; +static const int32_t V0_COMPRESSION_COOKIE = 0x1c849309; + +static const int32_t V1_ENCODING_COOKIE = 0x1c849301; +static const int32_t V1_COMPRESSION_COOKIE = 0x1c849302; + +static const int32_t V2_ENCODING_COOKIE = 0x1c849303; +static const int32_t V2_COMPRESSION_COOKIE = 0x1c849304; + +static int32_t get_cookie_base(int32_t cookie) +{ + return (cookie & ~0xf0); +} + +static int32_t word_size_from_cookie(int32_t cookie) +{ + return (cookie & 0xf0) >> 4; +} + +const char* hdr_strerror(int errnum) +{ + switch (errnum) + { + case HDR_COMPRESSION_COOKIE_MISMATCH: + return "Compression cookie mismatch"; + case HDR_ENCODING_COOKIE_MISMATCH: + return "Encoding cookie mismatch"; + case HDR_DEFLATE_INIT_FAIL: + return "Deflate initialisation failed"; + case HDR_DEFLATE_FAIL: + return "Deflate failed"; + case HDR_INFLATE_INIT_FAIL: + return "Inflate initialisation failed"; + case HDR_INFLATE_FAIL: + return "Inflate failed"; + case HDR_LOG_INVALID_VERSION: + return "Log - invalid version in log header"; + case HDR_TRAILING_ZEROS_INVALID: + return "Invalid number of trailing zeros"; + case HDR_VALUE_TRUNCATED: + return "Truncated value found when decoding"; + case HDR_ENCODED_INPUT_TOO_LONG: + return "The encoded input exceeds the size of the histogram"; + default: + return strerror(errnum); + } +} + +static void strm_init(z_stream* strm) +{ + strm->zfree = NULL; + strm->zalloc = NULL; + strm->opaque = NULL; + strm->next_in = NULL; + strm->avail_in = 0; +} + +union uint64_dbl_cvt +{ + uint64_t l; + double d; +}; + +static double int64_bits_to_double(int64_t i) +{ + union uint64_dbl_cvt x; + + x.l = (uint64_t) i; + return x.d; +} + +static uint64_t double_to_int64_bits(double d) +{ + union uint64_dbl_cvt x; + + x.d = d; + return x.l; +} + +#pragma pack(push, 1) +typedef struct /*__attribute__((__packed__))*/ +{ + int32_t cookie; + int32_t significant_figures; + int64_t lowest_trackable_value; + int64_t highest_trackable_value; + int64_t total_count; + int64_t counts[1]; +} _encoding_flyweight_v0; + +typedef struct /*__attribute__((__packed__))*/ +{ + int32_t cookie; + int32_t payload_len; + int32_t normalizing_index_offset; + int32_t significant_figures; + int64_t lowest_trackable_value; + int64_t highest_trackable_value; + uint64_t conversion_ratio_bits; + uint8_t counts[1]; +} _encoding_flyweight_v1; + +typedef struct /*__attribute__((__packed__))*/ +{ + int32_t cookie; + int32_t length; + uint8_t data[1]; +} _compression_flyweight; +#pragma pack(pop) + +#define SIZEOF_ENCODING_FLYWEIGHT_V0 (sizeof(_encoding_flyweight_v0) - sizeof(int64_t)) +#define SIZEOF_ENCODING_FLYWEIGHT_V1 (sizeof(_encoding_flyweight_v1) - sizeof(uint8_t)) +#define SIZEOF_COMPRESSION_FLYWEIGHT (sizeof(_compression_flyweight) - sizeof(uint8_t)) + +int hdr_encode_compressed( + struct hdr_histogram* h, + uint8_t** compressed_histogram, + size_t* compressed_len) +{ + _encoding_flyweight_v1* encoded = NULL; + _compression_flyweight* compressed = NULL; + int i; + int result = 0; + int data_index = 0; + int32_t payload_len; + uLong encoded_size; + uLongf dest_len; + size_t compressed_size; + + int32_t len_to_max = counts_index_for(h, h->max_value) + 1; + int32_t counts_limit = len_to_max < h->counts_len ? len_to_max : h->counts_len; + + const size_t encoded_len = SIZEOF_ENCODING_FLYWEIGHT_V1 + MAX_BYTES_LEB128 * (size_t) counts_limit; + if ((encoded = (_encoding_flyweight_v1*) calloc(encoded_len, sizeof(uint8_t))) == NULL) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + for (i = 0; i < counts_limit;) + { + int64_t value = h->counts[i]; + i++; + + if (value == 0) + { + int32_t zeros = 1; + + while (i < counts_limit && 0 == h->counts[i]) + { + zeros++; + i++; + } + + data_index += zig_zag_encode_i64(&encoded->counts[data_index], -zeros); + } + else + { + data_index += zig_zag_encode_i64(&encoded->counts[data_index], value); + } + } + + payload_len = data_index; + encoded_size = SIZEOF_ENCODING_FLYWEIGHT_V1 + data_index; + + encoded->cookie = htobe32(V2_ENCODING_COOKIE | 0x10); + encoded->payload_len = htobe32(payload_len); + encoded->normalizing_index_offset = htobe32(h->normalizing_index_offset); + encoded->significant_figures = htobe32(h->significant_figures); + encoded->lowest_trackable_value = htobe64(h->lowest_trackable_value); + encoded->highest_trackable_value = htobe64(h->highest_trackable_value); + encoded->conversion_ratio_bits = htobe64(double_to_int64_bits(h->conversion_ratio)); + + + /* Estimate the size of the compressed histogram. */ + dest_len = compressBound(encoded_size); + compressed_size = SIZEOF_COMPRESSION_FLYWEIGHT + dest_len; + + if ((compressed = (_compression_flyweight*) malloc(compressed_size)) == NULL) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + if (Z_OK != compress(compressed->data, &dest_len, (Bytef*) encoded, encoded_size)) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_DEFLATE_FAIL); + } + + compressed->cookie = htobe32(V2_COMPRESSION_COOKIE | 0x10); + compressed->length = htobe32((int32_t)dest_len); + + *compressed_histogram = (uint8_t*) compressed; + *compressed_len = SIZEOF_COMPRESSION_FLYWEIGHT + dest_len; + + cleanup: + free(encoded); + if (result == HDR_DEFLATE_FAIL) + { + free(compressed); + } + + return result; +} + +/* ######## ######## ###### ####### ######## #### ## ## ###### */ +/* ## ## ## ## ## ## ## ## ## ## ### ## ## ## */ +/* ## ## ## ## ## ## ## ## ## #### ## ## */ +/* ## ## ###### ## ## ## ## ## ## ## ## ## ## #### */ +/* ## ## ## ## ## ## ## ## ## ## #### ## ## */ +/* ## ## ## ## ## ## ## ## ## ## ## ### ## ## */ +/* ######## ######## ###### ####### ######## #### ## ## ###### */ + +static void _apply_to_counts_16(struct hdr_histogram* h, const int16_t* counts_data, const int32_t counts_limit) +{ + int i; + for (i = 0; i < counts_limit; i++) + { + h->counts[i] = be16toh(counts_data[i]); + } +} + +static void _apply_to_counts_32(struct hdr_histogram* h, const int32_t* counts_data, const int32_t counts_limit) +{ + int i; + for (i = 0; i < counts_limit; i++) + { + h->counts[i] = be32toh(counts_data[i]); + } +} + +static void _apply_to_counts_64(struct hdr_histogram* h, const int64_t* counts_data, const int32_t counts_limit) +{ + int i; + for (i = 0; i < counts_limit; i++) + { + h->counts[i] = be64toh(counts_data[i]); + } +} + +static int _apply_to_counts_zz(struct hdr_histogram* h, const uint8_t* counts_data, const int32_t data_limit) +{ + int64_t data_index = 0; + int32_t counts_index = 0; + int64_t value; + + while (data_index < data_limit && counts_index < h->counts_len) + { + data_index += zig_zag_decode_i64(&counts_data[data_index], &value); + + if (value < 0) + { + int64_t zeros = -value; + + if (value <= INT32_MIN || counts_index + zeros > h->counts_len) + { + return HDR_TRAILING_ZEROS_INVALID; + } + + counts_index += (int32_t) zeros; + } + else + { + h->counts[counts_index] = value; + counts_index++; + } + } + + if (data_index > data_limit) + { + return HDR_VALUE_TRUNCATED; + } + else if (data_index < data_limit) + { + return HDR_ENCODED_INPUT_TOO_LONG; + } + + return 0; +} + +static int _apply_to_counts( + struct hdr_histogram* h, const int32_t word_size, const uint8_t* counts_data, const int32_t counts_limit) +{ + switch (word_size) + { + case 2: + _apply_to_counts_16(h, (const int16_t*) counts_data, counts_limit); + return 0; + + case 4: + _apply_to_counts_32(h, (const int32_t*) counts_data, counts_limit); + return 0; + + case 8: + _apply_to_counts_64(h, (const int64_t*) counts_data, counts_limit); + return 0; + + case 1: + return _apply_to_counts_zz(h, counts_data, counts_limit); + + default: + return -1; + } +} + +static int hdr_decode_compressed_v0( + _compression_flyweight* compression_flyweight, + size_t length, + struct hdr_histogram** histogram) +{ + struct hdr_histogram* h = NULL; + int result = 0; + uint8_t* counts_array = NULL; + _encoding_flyweight_v0 encoding_flyweight; + z_stream strm; + int32_t compressed_len, encoding_cookie, word_size, significant_figures, counts_array_len; + int64_t lowest_trackable_value, highest_trackable_value; + + strm_init(&strm); + if (inflateInit(&strm) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + compressed_len = be32toh(compression_flyweight->length); + + if (compressed_len < 0 || (length - SIZEOF_COMPRESSION_FLYWEIGHT) < (size_t)compressed_len) + { + FAIL_AND_CLEANUP(cleanup, result, EINVAL); + } + + strm.next_in = compression_flyweight->data; + strm.avail_in = (uInt) compressed_len; + strm.next_out = (uint8_t *) &encoding_flyweight; + strm.avail_out = SIZEOF_ENCODING_FLYWEIGHT_V0; + + if (inflate(&strm, Z_SYNC_FLUSH) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + encoding_cookie = get_cookie_base(be32toh(encoding_flyweight.cookie)); + if (V0_ENCODING_COOKIE != encoding_cookie) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_ENCODING_COOKIE_MISMATCH); + } + + word_size = word_size_from_cookie(be32toh(encoding_flyweight.cookie)); + lowest_trackable_value = be64toh(encoding_flyweight.lowest_trackable_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + if (hdr_init( + lowest_trackable_value, + highest_trackable_value, + significant_figures, + &h) != 0) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + counts_array_len = h->counts_len * word_size; + if ((counts_array = (uint8_t*) calloc(1, (size_t) counts_array_len)) == NULL) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + strm.next_out = counts_array; + strm.avail_out = (uInt) counts_array_len; + + if (inflate(&strm, Z_FINISH) != Z_STREAM_END) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + _apply_to_counts(h, word_size, counts_array, h->counts_len); + + hdr_reset_internal_counters(h); + h->normalizing_index_offset = 0; + h->conversion_ratio = 1.0; + +cleanup: + (void)inflateEnd(&strm); + free(counts_array); + + if (result != 0) + { + free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + free(h); + } + + return result; +} + +static int hdr_decode_compressed_v1( + _compression_flyweight* compression_flyweight, + size_t length, + struct hdr_histogram** histogram) +{ + struct hdr_histogram* h = NULL; + int result = 0; + uint8_t* counts_array = NULL; + _encoding_flyweight_v1 encoding_flyweight; + z_stream strm; + int32_t compressed_length, word_size, significant_figures, counts_limit, encoding_cookie, counts_array_len; + int64_t lowest_trackable_value, highest_trackable_value; + + strm_init(&strm); + if (inflateInit(&strm) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + compressed_length = be32toh(compression_flyweight->length); + + if (compressed_length < 0 || length - SIZEOF_COMPRESSION_FLYWEIGHT < (size_t)compressed_length) + { + FAIL_AND_CLEANUP(cleanup, result, EINVAL); + } + + strm.next_in = compression_flyweight->data; + strm.avail_in = (uInt) compressed_length; + strm.next_out = (uint8_t *) &encoding_flyweight; + strm.avail_out = SIZEOF_ENCODING_FLYWEIGHT_V1; + + if (inflate(&strm, Z_SYNC_FLUSH) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + encoding_cookie = get_cookie_base(be32toh(encoding_flyweight.cookie)); + if (V1_ENCODING_COOKIE != encoding_cookie) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_ENCODING_COOKIE_MISMATCH); + } + + word_size = word_size_from_cookie(be32toh(encoding_flyweight.cookie)); + counts_limit = be32toh(encoding_flyweight.payload_len) / word_size; + lowest_trackable_value = be64toh(encoding_flyweight.lowest_trackable_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + if (hdr_init( + lowest_trackable_value, + highest_trackable_value, + significant_figures, + &h) != 0) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + /* Give the temp uncompressed array a little bif of extra */ + counts_array_len = counts_limit * word_size; + + if ((counts_array = (uint8_t*) calloc(1, (size_t) counts_array_len)) == NULL) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + strm.next_out = counts_array; + strm.avail_out = (uInt) counts_array_len; + + if (inflate(&strm, Z_FINISH) != Z_STREAM_END) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + _apply_to_counts(h, word_size, counts_array, counts_limit); + + h->normalizing_index_offset = be32toh(encoding_flyweight.normalizing_index_offset); + h->conversion_ratio = int64_bits_to_double(be64toh(encoding_flyweight.conversion_ratio_bits)); + hdr_reset_internal_counters(h); + +cleanup: + (void)inflateEnd(&strm); + free(counts_array); + + if (result != 0) + { + free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + free(h); + } + + return result; +} + +static int hdr_decode_compressed_v2( + _compression_flyweight* compression_flyweight, + size_t length, + struct hdr_histogram** histogram) +{ + struct hdr_histogram* h = NULL; + int result = 0; + int rc = 0; + uint8_t* counts_array = NULL; + _encoding_flyweight_v1 encoding_flyweight; + z_stream strm; + int32_t compressed_length, encoding_cookie, counts_limit, significant_figures; + int64_t lowest_trackable_value, highest_trackable_value; + + strm_init(&strm); + if (inflateInit(&strm) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + compressed_length = be32toh(compression_flyweight->length); + + if (compressed_length < 0 || length - SIZEOF_COMPRESSION_FLYWEIGHT < (size_t)compressed_length) + { + FAIL_AND_CLEANUP(cleanup, result, EINVAL); + } + + strm.next_in = compression_flyweight->data; + strm.avail_in = (uInt) compressed_length; + strm.next_out = (uint8_t *) &encoding_flyweight; + strm.avail_out = SIZEOF_ENCODING_FLYWEIGHT_V1; + + if (inflate(&strm, Z_SYNC_FLUSH) != Z_OK) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + encoding_cookie = get_cookie_base(be32toh(encoding_flyweight.cookie)); + if (V2_ENCODING_COOKIE != encoding_cookie) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_ENCODING_COOKIE_MISMATCH); + } + + counts_limit = be32toh(encoding_flyweight.payload_len); + lowest_trackable_value = be64toh(encoding_flyweight.lowest_trackable_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + rc = hdr_init(lowest_trackable_value, highest_trackable_value, significant_figures, &h); + if (rc) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + /* Make sure there at least 9 bytes to read */ + /* if there is a corrupt value at the end */ + /* of the array we won't read corrupt data or crash. */ + if ((counts_array = (uint8_t*) calloc(1, (size_t) counts_limit + 9)) == NULL) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + strm.next_out = counts_array; + strm.avail_out = (uInt) counts_limit; + + if (inflate(&strm, Z_FINISH) != Z_STREAM_END) + { + FAIL_AND_CLEANUP(cleanup, result, HDR_INFLATE_FAIL); + } + + rc = _apply_to_counts_zz(h, counts_array, counts_limit); + if (rc) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + h->normalizing_index_offset = be32toh(encoding_flyweight.normalizing_index_offset); + h->conversion_ratio = int64_bits_to_double(be64toh(encoding_flyweight.conversion_ratio_bits)); + hdr_reset_internal_counters(h); + +cleanup: + (void)inflateEnd(&strm); + free(counts_array); + + if (result != 0) + { + free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + free(h); + } + + return result; +} + +int hdr_decode_compressed( + uint8_t* buffer, size_t length, struct hdr_histogram** histogram) +{ + int32_t compression_cookie; + _compression_flyweight* compression_flyweight; + + if (length < SIZEOF_COMPRESSION_FLYWEIGHT) + { + return EINVAL; + } + + compression_flyweight = (_compression_flyweight*) buffer; + + compression_cookie = get_cookie_base(be32toh(compression_flyweight->cookie)); + if (V0_COMPRESSION_COOKIE == compression_cookie) + { + return hdr_decode_compressed_v0(compression_flyweight, length, histogram); + } + else if (V1_COMPRESSION_COOKIE == compression_cookie) + { + return hdr_decode_compressed_v1(compression_flyweight, length, histogram); + } + else if (V2_COMPRESSION_COOKIE == compression_cookie) + { + return hdr_decode_compressed_v2(compression_flyweight, length, histogram); + } + + return HDR_COMPRESSION_COOKIE_MISMATCH; +} + +/* ## ## ######## #### ######## ######## ######## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ######## ## ## ###### ######## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ### ### ## ## #### ## ######## ## ## */ + +int hdr_log_writer_init(struct hdr_log_writer* writer) +{ + (void)writer; + return 0; +} + +#define LOG_VERSION "1.2" +#define LOG_MAJOR_VERSION 1 + +static int print_user_prefix(FILE* f, const char* prefix) +{ + if (!prefix) + { + return 0; + } + + return fprintf(f, "#[%s]\n", prefix); +} + +static int print_version(FILE* f, const char* version) +{ + return fprintf(f, "#[Histogram log format version %s]\n", version); +} + +static int print_time(FILE* f, hdr_timespec* timestamp) +{ + char time_str[128]; + struct tm date_time; + + if (!timestamp) + { + return 0; + } + +#if defined(__WINDOWS__) + _gmtime32_s(&date_time, ×tamp->tv_sec); +#else + gmtime_r(×tamp->tv_sec, &date_time); +#endif + + strftime(time_str, 128, "%a %b %X %Z %Y", &date_time); + + return fprintf( + f, "#[StartTime: %.3f (seconds since epoch), %s]\n", + hdr_timespec_as_double(timestamp), time_str); +} + +static int print_header(FILE* f) +{ + return fprintf(f, "\"StartTimestamp\",\"EndTimestamp\",\"Interval_Max\",\"Interval_Compressed_Histogram\"\n"); +} + +/* Example log */ +/* #[Logged with jHiccup version 2.0.3-SNAPSHOT] */ +/* #[Histogram log format version 1.01] */ +/* #[StartTime: 1403476110.183 (seconds since epoch), Mon Jun 23 10:28:30 NZST 2014] */ +/* "StartTimestamp","EndTimestamp","Interval_Max","Interval_Compressed_Histogram" */ +int hdr_log_write_header( + struct hdr_log_writer* writer, FILE* file, + const char* user_prefix, hdr_timespec* timestamp) +{ + (void)writer; + + if (print_user_prefix(file, user_prefix) < 0) + { + return EIO; + } + if (print_version(file, LOG_VERSION) < 0) + { + return EIO; + } + if (print_time(file, timestamp) < 0) + { + return EIO; + } + if (print_header(file) < 0) + { + return EIO; + } + + return 0; +} + +int hdr_log_write( + struct hdr_log_writer* writer, + FILE* file, + const hdr_timespec* start_timestamp, + const hdr_timespec* end_timestamp, + struct hdr_histogram* histogram) +{ + uint8_t* compressed_histogram = NULL; + size_t compressed_len = 0; + char* encoded_histogram = NULL; + int rc = 0; + int result = 0; + size_t encoded_len; + + (void)writer; + + rc = hdr_encode_compressed(histogram, &compressed_histogram, &compressed_len); + if (rc != 0) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + encoded_len = hdr_base64_encoded_len(compressed_len); + encoded_histogram = (char*) calloc(encoded_len + 1, sizeof(char)); + + rc = hdr_base64_encode( + compressed_histogram, compressed_len, encoded_histogram, encoded_len); + if (rc != 0) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + if (fprintf( + file, "%.3f,%.3f,%" PRIu64 ".0,%s\n", + hdr_timespec_as_double(start_timestamp), + hdr_timespec_as_double(end_timestamp), + hdr_max(histogram), + encoded_histogram) < 0) + { + result = EIO; + } + +cleanup: + free(compressed_histogram); + free(encoded_histogram); + + return result; +} + +/* ######## ######## ### ######## ######## ######## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ######## ###### ## ## ## ## ###### ######## */ +/* ## ## ## ######### ## ## ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ## */ +/* ## ## ######## ## ## ######## ######## ## ## */ + +int hdr_log_reader_init(struct hdr_log_reader* reader) +{ + reader->major_version = 0; + reader->minor_version = 0; + reader->start_timestamp.tv_sec = 0; + reader->start_timestamp.tv_nsec = 0; + + return 0; +} + +static void scan_log_format(struct hdr_log_reader* reader, const char* line) +{ + const char* format = "#[Histogram log format version %d.%d]"; + sscanf(line, format, &reader->major_version, &reader->minor_version); +} + +static void scan_start_time(struct hdr_log_reader* reader, const char* line) +{ + const char* format = "#[StartTime: %lf [^\n]"; + double timestamp = 0.0; + + if (sscanf(line, format, ×tamp) == 1) + { + hdr_timespec_from_double(&reader->start_timestamp, timestamp); + } +} + +static void scan_header_line(struct hdr_log_reader* reader, const char* line) +{ + scan_log_format(reader, line); + scan_start_time(reader, line); +} + +static bool validate_log_version(struct hdr_log_reader* reader) +{ + return reader->major_version == LOG_MAJOR_VERSION && + (reader->minor_version == 0 || reader->minor_version == 1 || + reader->minor_version == 2 || reader->minor_version == 3); +} + +#define HEADER_LINE_LENGTH 128 + +int hdr_log_read_header(struct hdr_log_reader* reader, FILE* file) +{ + char line[HEADER_LINE_LENGTH]; /* TODO: check for overflow. */ + + bool parsing_header = true; + + do + { + int c = fgetc(file); + ungetc(c, file); + + switch (c) + { + + case '#': + if (fgets(line, HEADER_LINE_LENGTH, file) == NULL) + { + return EIO; + } + + scan_header_line(reader, line); + break; + + case '"': + if (fgets(line, HEADER_LINE_LENGTH, file) == NULL) + { + return EIO; + } + + parsing_header = false; + break; + + default: + parsing_header = false; + } + } + while (parsing_header); + + if (!validate_log_version(reader)) + { + return HDR_LOG_INVALID_VERSION; + } + + return 0; +} + +static void update_timespec(hdr_timespec* ts, double timestamp) +{ + if (NULL == ts) + { + return; + } + + hdr_timespec_from_double(ts, timestamp); +} + +#if defined(_MSC_VER) + +static ssize_t hdr_read_chunk(char* buffer, size_t length, char terminator, FILE* stream) +{ + if (buffer == NULL || length == 0) + { + return -1; + } + + for (size_t i = 0; i < length; ++i) + { + int c = fgetc(stream); + buffer[i] = (char)c; + if (c == (int) '\0' || c == (int) terminator || c == EOF) + { + buffer[i] = '\0'; + return i; + } + } + + return length; +} + +/* Note that this version of getline assumes lineptr is valid. */ +static ssize_t hdr_getline(char** lineptr, FILE* stream) +{ + if (stream == NULL) + { + return -1; + } + + size_t allocation = 128; + size_t used = 0; + + char* scratch = NULL; + for (;;) + { + allocation += allocation; + + char* before = scratch; + scratch = realloc(scratch, allocation); + if (scratch == NULL) + { + if (before) + { + free(before); + } + return -1; + } + + size_t wanted = allocation - used - 1; + size_t read_length = hdr_read_chunk(scratch + used, wanted, '\n', stream); + used += read_length; + + + if (read_length < wanted || scratch[used - 1] == '\n' || scratch[used - 1] == '\0') + { + scratch[used] = '\0'; + *lineptr = scratch; + return used; + } + } +} + +#else +static ssize_t hdr_getline(char** lineptr, FILE* stream) +{ + size_t line_length = 0; + return getline(lineptr, &line_length, stream); +} +#endif + +int hdr_log_read( + struct hdr_log_reader* reader, FILE* file, struct hdr_histogram** histogram, + hdr_timespec* timestamp, hdr_timespec* interval) +{ + const char* format_v12 = "%lf,%lf,%d.%d,%s"; + const char* format_v13 = "Tag=%*[^,],%lf,%lf,%d.%d,%s"; + char* base64_histogram = NULL; + uint8_t* compressed_histogram = NULL; + char* line = NULL; + int result = 0; + ssize_t read; + size_t base64_len, compressed_len; + int r; + int interval_max_s = 0; + int interval_max_ms = 0; + int num_tokens; + + double begin_timestamp = 0.0; + double end_timestamp = 0.0; + + (void)reader; + + read = hdr_getline(&line, file); + if (-1 == read) + { + if (0 == errno) + { + FAIL_AND_CLEANUP(cleanup, result, EOF); + } + else + { + FAIL_AND_CLEANUP(cleanup, result, EIO); + } + } + + null_trailing_whitespace(line, read); + if (strlen(line) == 0) + { + FAIL_AND_CLEANUP(cleanup, result, EOF); + } + + r = realloc_buffer((void**)&base64_histogram, sizeof(char), read); + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + r = realloc_buffer((void**)&compressed_histogram, sizeof(uint8_t), read); + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, ENOMEM); + } + + num_tokens = sscanf( + line, format_v13, &begin_timestamp, &end_timestamp, + &interval_max_s, &interval_max_ms, base64_histogram); + + if (num_tokens != 5) + { + num_tokens = sscanf( + line, format_v12, &begin_timestamp, &end_timestamp, + &interval_max_s, &interval_max_ms, base64_histogram); + + if (num_tokens != 5) + { + FAIL_AND_CLEANUP(cleanup, result, EINVAL); + } + } + + base64_len = strlen(base64_histogram); + compressed_len = hdr_base64_decoded_len(base64_len); + + r = hdr_base64_decode( + base64_histogram, base64_len, compressed_histogram, compressed_len); + + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, r); + } + + r = hdr_decode_compressed(compressed_histogram, compressed_len, histogram); + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, r); + } + + update_timespec(timestamp, begin_timestamp); + update_timespec(interval, end_timestamp); + +cleanup: + free(line); + free(base64_histogram); + free(compressed_histogram); + + return result; +} + +int hdr_log_encode(struct hdr_histogram* histogram, char** encoded_histogram) +{ + char *encoded_histogram_tmp = NULL; + uint8_t* compressed_histogram = NULL; + size_t compressed_len = 0; + int rc = 0; + int result = 0; + size_t encoded_len; + + rc = hdr_encode_compressed(histogram, &compressed_histogram, &compressed_len); + if (rc != 0) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + encoded_len = hdr_base64_encoded_len(compressed_len); + encoded_histogram_tmp = (char*) calloc(encoded_len + 1, sizeof(char)); + + rc = hdr_base64_encode( + compressed_histogram, compressed_len, encoded_histogram_tmp, encoded_len); + if (rc != 0) + { + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + *encoded_histogram = encoded_histogram_tmp; + +cleanup: + free(compressed_histogram); + + return result; +} + +int hdr_log_decode(struct hdr_histogram** histogram, char* base64_histogram, size_t base64_len) +{ + int r; + uint8_t* compressed_histogram = NULL; + int result = 0; + + size_t compressed_len = hdr_base64_decoded_len(base64_len); + compressed_histogram = (uint8_t*) malloc(sizeof(uint8_t)*compressed_len); + memset(compressed_histogram, 0, compressed_len); + + r = hdr_base64_decode( + base64_histogram, base64_len, compressed_histogram, compressed_len); + + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, r); + } + + r = hdr_decode_compressed(compressed_histogram, compressed_len, histogram); + if (r != 0) + { + FAIL_AND_CLEANUP(cleanup, result, r); + } + +cleanup: + free(compressed_histogram); + + return result; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/src/hdr_histogram_log.h b/src/hdr_histogram_log.h new file mode 100644 index 0000000..564bf03 --- /dev/null +++ b/src/hdr_histogram_log.h @@ -0,0 +1,166 @@ +/** + * hdr_histogram_log.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + * + * The implementation makes use of zlib to provide compression. You will need + * to link against -lz in order to link applications that include this header. + */ + +#ifndef HDR_HISTOGRAM_H_LOG +#define HDR_HISTOGRAM_H_LOG 1 + +#define HDR_COMPRESSION_COOKIE_MISMATCH -29999 +#define HDR_ENCODING_COOKIE_MISMATCH -29998 +#define HDR_DEFLATE_INIT_FAIL -29997 +#define HDR_DEFLATE_FAIL -29996 +#define HDR_INFLATE_INIT_FAIL -29995 +#define HDR_INFLATE_FAIL -29994 +#define HDR_LOG_INVALID_VERSION -29993 +#define HDR_TRAILING_ZEROS_INVALID -29992 +#define HDR_VALUE_TRUNCATED -29991 +#define HDR_ENCODED_INPUT_TOO_LONG -29990 + +#include +#include +#include + +#include "hdr_time.h" +#include "hdr_histogram.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Encode and compress the histogram with gzip. + */ +int hdr_log_encode(struct hdr_histogram* histogram, char** encoded_histogram); + +/** + * Decode and decompress the histogram with gzip. + */ +int hdr_log_decode(struct hdr_histogram** histogram, char* base64_histogram, size_t base64_len); + +struct hdr_log_writer +{ + uint32_t nonce; +}; + +/** + * Initialise the log writer. + * + * @param writer 'This' pointer + * @return 0 on success. + */ +int hdr_log_writer_init(struct hdr_log_writer* writer); + +/** + * Write the header to the log, this will constist of a user defined string, + * the current timestamp, version information and the CSV header. + * + * @param writer 'This' pointer + * @param file The stream to output the log header to. + * @param user_prefix User defined string to include in the header. + * @param timestamp The start time that the histogram started recording from. + * @return Will return 0 if it successfully completed or an error number if there + * was a failure. EIO if the write failed. + */ +int hdr_log_write_header( + struct hdr_log_writer* writer, + FILE* file, + const char* user_prefix, + hdr_timespec* timestamp); + +/** + * Write an hdr_histogram entry to the log. It will be encoded in a similar + * fashion to the approach used by the Java version of the HdrHistogram. It will + * be a CSV line consisting of ,,, + * where is the binary histogram gzip compressed and base64 encoded. + * + * Timestamp is a bit of misnomer for the start_timestamp and end_timestamp values + * these could be offsets, e.g. start_timestamp could be offset from process start + * time and end_timestamp could actually be the length of the recorded interval. + * + * @param writer 'This' pointer + * @param file The stream to write the entry to. + * @param start_timestamp The start timestamp to include in the logged entry. + * @param end_timestamp The end timestamp to include in the logged entry. + * @param histogram The histogram to encode and log. + * @return Will return 0 if it successfully completed or an error number if there + * was a failure. Errors include HDR_DEFLATE_INIT_FAIL, HDR_DEFLATE_FAIL if + * something when wrong during gzip compression. ENOMEM if we failed to allocate + * or reallocate the buffer used for encoding (out of memory problem). EIO if + * write failed. + */ +int hdr_log_write( + struct hdr_log_writer* writer, + FILE* file, + const hdr_timespec* start_timestamp, + const hdr_timespec* end_timestamp, + struct hdr_histogram* histogram); + +struct hdr_log_reader +{ + int major_version; + int minor_version; + hdr_timespec start_timestamp; +}; + +/** + * Initialise the log reader. + * + * @param reader 'This' pointer + * @return 0 on success + */ +int hdr_log_reader_init(struct hdr_log_reader* reader); + +/** + * Reads the the header information from the log. Will capure information + * such as version number and start timestamp from the header. + * + * @param hdr_log_reader 'This' pointer + * @param file The data stream to read from. + * @return 0 on success. An error number on failure. + */ +int hdr_log_read_header(struct hdr_log_reader* reader, FILE* file); + +/** + * Reads an entry from the log filling in the specified histogram, timestamp and + * interval values. If the supplied pointer to the histogram for this method is + * NULL then a new histogram will be allocated for the caller, however it will + * become the callers responsibility to free it later. If the pointer is non-null + * the histogram read from the log will be merged with the supplied histogram. + * + * @param reader 'This' pointer + * @param file The stream to read the histogram from. + * @param histogram Pointer to allocate a histogram to or merge into. + * @param timestamp The first timestamp from the CSV entry. + * @param interval The second timestamp from the CSV entry + * @return Will return 0 on success or an error number if there was some wrong + * when reading in the histogram. EOF (-1) will indicate that there are no more + * histograms left to be read from 'file'. + * HDR_INFLATE_INIT_FAIL or HDR_INFLATE_FAIL if + * there was a problem with Gzip. HDR_COMPRESSION_COOKIE_MISMATCH or + * HDR_ENCODING_COOKIE_MISMATCH if the cookie values are incorrect. + * HDR_LOG_INVALID_VERSION if the log can not be parsed. ENOMEM if buffer space + * or the histogram can not be allocated. EIO if there was an error during + * the read. EINVAL in any input values are incorrect. + */ +int hdr_log_read( + struct hdr_log_reader* reader, FILE* file, struct hdr_histogram** histogram, + hdr_timespec* timestamp, hdr_timespec* interval); + +/** + * Returns a string representation of the error number. + * + * @param errnum The error response from a previous call. + * @return The user readable representation of the error. + */ +const char* hdr_strerror(int errnum); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hdr_interval_recorder.c b/src/hdr_interval_recorder.c new file mode 100644 index 0000000..67efa71 --- /dev/null +++ b/src/hdr_interval_recorder.c @@ -0,0 +1,221 @@ +/** + * hdr_interval_recorder.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include "hdr_atomic.h" +#include "hdr_interval_recorder.h" + +int hdr_interval_recorder_init(struct hdr_interval_recorder* r) +{ + r->active = r->inactive = NULL; + return hdr_writer_reader_phaser_init(&r->phaser); +} + +int hdr_interval_recorder_init_all( + struct hdr_interval_recorder* r, + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures) +{ + int result; + + r->active = r->inactive = NULL; + result = hdr_writer_reader_phaser_init(&r->phaser); + result = result == 0 + ? hdr_init(lowest_trackable_value, highest_trackable_value, significant_figures, &r->active) + : result; + + return result; +} + +void hdr_interval_recorder_destroy(struct hdr_interval_recorder* r) +{ + hdr_writer_reader_phaser_destroy(&r->phaser); + if (r->active) { + hdr_close(r->active); + } + if (r->inactive) { + hdr_close(r->inactive); + } +} + +struct hdr_histogram* hdr_interval_recorder_sample_and_recycle( + struct hdr_interval_recorder* r, + struct hdr_histogram* histogram_to_recycle) +{ + struct hdr_histogram* old_active; + + if (NULL == histogram_to_recycle) + { + int64_t lo = r->active->lowest_trackable_value; + int64_t hi = r->active->highest_trackable_value; + int significant_figures = r->active->significant_figures; + hdr_init(lo, hi, significant_figures, &histogram_to_recycle); + } + else + { + hdr_reset(histogram_to_recycle); + } + + hdr_phaser_reader_lock(&r->phaser); + + /* volatile read */ + old_active = hdr_atomic_load_pointer(&r->active); + + /* volatile write */ + hdr_atomic_store_pointer(&r->active, histogram_to_recycle); + + hdr_phaser_flip_phase(&r->phaser, 0); + + hdr_phaser_reader_unlock(&r->phaser); + + return old_active; +} + +struct hdr_histogram* hdr_interval_recorder_sample(struct hdr_interval_recorder* r) +{ + r->inactive = hdr_interval_recorder_sample_and_recycle(r, r->inactive); + return r->inactive; +} + +static void hdr_interval_recorder_update( + struct hdr_interval_recorder* r, + void(*update_action)(struct hdr_histogram*, void*), + void* arg) +{ + int64_t val = hdr_phaser_writer_enter(&r->phaser); + + void* active = hdr_atomic_load_pointer(&r->active); + + update_action(active, arg); + + hdr_phaser_writer_exit(&r->phaser, val); +} + +static void update_values(struct hdr_histogram* data, void* arg) +{ + struct hdr_histogram* h = data; + int64_t* params = arg; + params[2] = hdr_record_values(h, params[0], params[1]); +} + +static void update_values_atomic(struct hdr_histogram* data, void* arg) +{ + struct hdr_histogram* h = data; + int64_t* params = arg; + params[2] = hdr_record_values_atomic(h, params[0], params[1]); +} + +int64_t hdr_interval_recorder_record_values( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count +) +{ + int64_t params[3]; + params[0] = value; + params[1] = count; + params[2] = 0; + + hdr_interval_recorder_update(r, update_values, ¶ms[0]); + return params[2]; +} + +int64_t hdr_interval_recorder_record_value( + struct hdr_interval_recorder* r, + int64_t value +) +{ + return hdr_interval_recorder_record_values(r, value, 1); +} + +static void update_corrected_values(struct hdr_histogram* data, void* arg) +{ + struct hdr_histogram* h = data; + int64_t* params = arg; + params[3] = hdr_record_corrected_values(h, params[0], params[1], params[2]); +} + +static void update_corrected_values_atomic(struct hdr_histogram* data, void* arg) +{ + struct hdr_histogram* h = data; + int64_t* params = arg; + params[3] = hdr_record_corrected_values_atomic(h, params[0], params[1], params[2]); +} + +int64_t hdr_interval_recorder_record_corrected_values( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count, + int64_t expected_interval +) +{ + int64_t params[4]; + params[0] = value; + params[1] = count; + params[2] = expected_interval; + params[3] = 0; + + hdr_interval_recorder_update(r, update_corrected_values, ¶ms[0]); + return params[3]; +} + +int64_t hdr_interval_recorder_record_corrected_value( + struct hdr_interval_recorder* r, + int64_t value, + int64_t expected_interval +) +{ + return hdr_interval_recorder_record_corrected_values(r, value, 1, expected_interval); +} + +int64_t hdr_interval_recorder_record_value_atomic( + struct hdr_interval_recorder* r, + int64_t value +) +{ + return hdr_interval_recorder_record_values_atomic(r, value, 1); +} + +int64_t hdr_interval_recorder_record_values_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count +) +{ + int64_t params[3]; + params[0] = value; + params[1] = count; + params[2] = 0; + + hdr_interval_recorder_update(r, update_values_atomic, ¶ms[0]); + return params[2]; +} + +int64_t hdr_interval_recorder_record_corrected_value_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t expected_interval +) +{ + return hdr_interval_recorder_record_corrected_values_atomic(r, value, 1, expected_interval); +} + +int64_t hdr_interval_recorder_record_corrected_values_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count, + int64_t expected_interval +) +{ + int64_t params[4]; + params[0] = value; + params[1] = count; + params[2] = expected_interval; + params[3] = 0; + + hdr_interval_recorder_update(r, update_corrected_values_atomic, ¶ms[0]); + return params[3]; +} diff --git a/src/hdr_interval_recorder.h b/src/hdr_interval_recorder.h new file mode 100644 index 0000000..91ceaec --- /dev/null +++ b/src/hdr_interval_recorder.h @@ -0,0 +1,109 @@ +/** + * hdr_interval_recorder.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_INTERVAL_RECORDER_H +#define HDR_INTERVAL_RECORDER_H 1 + +#include "hdr_writer_reader_phaser.h" +#include "hdr_histogram.h" + +HDR_ALIGN_PREFIX(8) +struct hdr_interval_recorder +{ + struct hdr_histogram* active; + struct hdr_histogram* inactive; + struct hdr_writer_reader_phaser phaser; +} +HDR_ALIGN_SUFFIX(8); + +#ifdef __cplusplus +extern "C" { +#endif + +int hdr_interval_recorder_init(struct hdr_interval_recorder* r); + +int hdr_interval_recorder_init_all( + struct hdr_interval_recorder* r, + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures); + +void hdr_interval_recorder_destroy(struct hdr_interval_recorder* r); + +int64_t hdr_interval_recorder_record_value( + struct hdr_interval_recorder* r, + int64_t value +); + +int64_t hdr_interval_recorder_record_values( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count +); + +int64_t hdr_interval_recorder_record_corrected_value( + struct hdr_interval_recorder* r, + int64_t value, + int64_t expected_interval +); + +int64_t hdr_interval_recorder_record_corrected_values( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count, + int64_t expected_interval +); + +int64_t hdr_interval_recorder_record_value_atomic( + struct hdr_interval_recorder* r, + int64_t value +); + +int64_t hdr_interval_recorder_record_values_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count +); + +int64_t hdr_interval_recorder_record_corrected_value_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t expected_interval +); + +int64_t hdr_interval_recorder_record_corrected_values_atomic( + struct hdr_interval_recorder* r, + int64_t value, + int64_t count, + int64_t expected_interval +); + +/** + * The is generally the preferred approach for recylcing histograms through + * the recorder as it is safe when used from callers in multiple threads and + * the returned histogram won't automatically become active without being + * passed back into this method. + * + * @param r 'this' recorder + * @param histogram_to_recycle + * @return the histogram that was previous being recorded to. + */ +struct hdr_histogram* hdr_interval_recorder_sample_and_recycle( + struct hdr_interval_recorder* r, + struct hdr_histogram* histogram_to_recycle); + +/** + * @deprecated Prefer hdr_interval_recorder_sample_and_recycle + * @param r 'this' recorder + * @return the histogram that was previous being recorded to. + */ +struct hdr_histogram* hdr_interval_recorder_sample(struct hdr_interval_recorder* r); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hdr_tests.h b/src/hdr_tests.h new file mode 100644 index 0000000..c016d3a --- /dev/null +++ b/src/hdr_tests.h @@ -0,0 +1,22 @@ +#ifndef HDR_TESTS_H +#define HDR_TESTS_H + +/* These are functions used in tests and are not intended for normal usage. */ + +#include "hdr_histogram.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t counts_index_for(const struct hdr_histogram* h, int64_t value); +int hdr_encode_compressed(struct hdr_histogram* h, uint8_t** compressed_histogram, size_t* compressed_len); +int hdr_decode_compressed(uint8_t* buffer, size_t length, struct hdr_histogram** histogram); +void hdr_base64_decode_block(const char* input, uint8_t* output); +void hdr_base64_encode_block(const uint8_t* input, char* output); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hdr_thread.c b/src/hdr_thread.c new file mode 100644 index 0000000..721780e --- /dev/null +++ b/src/hdr_thread.c @@ -0,0 +1,102 @@ +/** +* hdr_thread.c +* Written by Philip Orwig and released to the public domain, +* as explained at http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#include +#include "hdr_thread.h" + +struct hdr_mutex* hdr_mutex_alloc(void) +{ + return malloc(sizeof(hdr_mutex)); +} + +void hdr_mutex_free(struct hdr_mutex* mutex) +{ + free(mutex); +} + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include + +int hdr_mutex_init(struct hdr_mutex* mutex) +{ + InitializeCriticalSection((CRITICAL_SECTION*)(mutex->_critical_section)); + return 0; +} + +void hdr_mutex_destroy(struct hdr_mutex* mutex) +{ + DeleteCriticalSection((CRITICAL_SECTION*)(mutex->_critical_section)); +} + +void hdr_mutex_lock(struct hdr_mutex* mutex) +{ + EnterCriticalSection((CRITICAL_SECTION*)(mutex->_critical_section)); +} + +void hdr_mutex_unlock(struct hdr_mutex* mutex) +{ + LeaveCriticalSection((CRITICAL_SECTION*)(mutex->_critical_section)); +} + +void hdr_yield() +{ + Sleep(0); +} + +int hdr_usleep(unsigned int useconds) +{ + struct timeval tv; + + tv.tv_sec = (long)useconds / 1000000; + tv.tv_usec = useconds % 1000000; + select(0, NULL, NULL, NULL, &tv); + + return 0; +} + + +#else +#include +#include + +int hdr_mutex_init(struct hdr_mutex* mutex) +{ + return pthread_mutex_init(&mutex->_mutex, NULL); +} + +void hdr_mutex_destroy(struct hdr_mutex* mutex) +{ + pthread_mutex_destroy(&mutex->_mutex); +} + +void hdr_mutex_lock(struct hdr_mutex* mutex) +{ + pthread_mutex_lock(&mutex->_mutex); +} + +void hdr_mutex_unlock(struct hdr_mutex* mutex) +{ + pthread_mutex_unlock(&mutex->_mutex); +} + +void hdr_yield() +{ + sched_yield(); +} + +int hdr_usleep(unsigned int useconds) +{ + return usleep(useconds); +} + + +#endif diff --git a/src/hdr_thread.h b/src/hdr_thread.h new file mode 100644 index 0000000..549a848 --- /dev/null +++ b/src/hdr_thread.h @@ -0,0 +1,55 @@ +/** + * hdr_thread.h + * Written by Philip Orwig and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_THREAD_H__ +#define HDR_THREAD_H__ + +#include + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + + +#define HDR_ALIGN_PREFIX(alignment) __declspec( align(alignment) ) +#define HDR_ALIGN_SUFFIX(alignment) + +typedef struct hdr_mutex +{ + uint8_t _critical_section[40]; +} hdr_mutex; + +#else + +#include + +#define HDR_ALIGN_PREFIX(alignment) +#define HDR_ALIGN_SUFFIX(alignment) __attribute__((aligned(alignment))) + +typedef struct hdr_mutex +{ + pthread_mutex_t _mutex; +} hdr_mutex; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct hdr_mutex* hdr_mutex_alloc(void); +void hdr_mutex_free(struct hdr_mutex*); + +int hdr_mutex_init(struct hdr_mutex* mutex); +void hdr_mutex_destroy(struct hdr_mutex* mutex); + +void hdr_mutex_lock(struct hdr_mutex* mutex); +void hdr_mutex_unlock(struct hdr_mutex* mutex); + +void hdr_yield(void); +int hdr_usleep(unsigned int useconds); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/hdr_time.c b/src/hdr_time.c new file mode 100644 index 0000000..0967d51 --- /dev/null +++ b/src/hdr_time.c @@ -0,0 +1,98 @@ +/** +* hdr_time.h +* Written by Michael Barker and Philip Orwig and released to the public domain, +* as explained at http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#include "hdr_time.h" + +#if defined(_WIN32) || defined(_WIN64) + +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +static int s_clockPeriodSet = 0; +static double s_clockPeriod = 1.0; + +void hdr_gettime(hdr_timespec* t) +{ + LARGE_INTEGER num; + /* if this is distasteful, we can add in an hdr_time_init() */ + if (!s_clockPeriodSet) + { + QueryPerformanceFrequency(&num); + s_clockPeriod = 1.0 / (double) num.QuadPart; + s_clockPeriodSet = 1; + } + + QueryPerformanceCounter(&num); + double seconds = num.QuadPart * s_clockPeriod; + double integral; + double remainder = modf(seconds, &integral); + + t->tv_sec = (long) integral; + t->tv_nsec = (long) (remainder * 1000000000); +} + +#elif defined(__APPLE__) + +#include +#include + + +void hdr_gettime(hdr_timespec* ts) +{ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +} + + +void hdr_getnow(hdr_timespec* ts) +{ + hdr_gettime(ts); +} + +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__OpenBSD__) + + +void hdr_gettime(hdr_timespec* t) +{ + clock_gettime(CLOCK_MONOTONIC, (struct timespec*)t); +} + +void hdr_getnow(hdr_timespec* t) +{ + clock_gettime(CLOCK_REALTIME, (struct timespec*)t); +} + +#else + +#warning "Platform not supported\n" + +#endif + +double hdr_timespec_as_double(const hdr_timespec* t) +{ + double d = t->tv_sec; + return d + (t->tv_nsec / 1000000000.0); +} + +void hdr_timespec_from_double(hdr_timespec* t, double value) +{ + int seconds = (int) value; + int milliseconds = (int) round((value - seconds) * 1000); + + t->tv_sec = seconds; + t->tv_nsec = milliseconds * 1000000; +} + + + diff --git a/src/hdr_time.h b/src/hdr_time.h new file mode 100644 index 0000000..feecee9 --- /dev/null +++ b/src/hdr_time.h @@ -0,0 +1,49 @@ +/** + * hdr_time.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_TIME_H__ +#define HDR_TIME_H__ + +#include +#include + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + +typedef struct hdr_timespec +{ + long tv_sec; + long tv_nsec; +} hdr_timespec; + +#else + +typedef struct timespec hdr_timespec; + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +void hdr_gettime(hdr_timespec* t); +#else +void hdr_gettime(hdr_timespec* t); +#endif + +void hdr_getnow(hdr_timespec* t); + +double hdr_timespec_as_double(const hdr_timespec* t); + +/* Assumes only millisecond accuracy. */ +void hdr_timespec_from_double(hdr_timespec* t, double value); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/hdr_writer_reader_phaser.c b/src/hdr_writer_reader_phaser.c new file mode 100644 index 0000000..15f3f2b --- /dev/null +++ b/src/hdr_writer_reader_phaser.c @@ -0,0 +1,137 @@ +/** + * hdr_writer_reader_phaser.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include + +#include "hdr_atomic.h" +#include "hdr_thread.h" + +#include "hdr_writer_reader_phaser.h" + +static int64_t _hdr_phaser_get_epoch(int64_t* field) +{ + return hdr_atomic_load_64(field); +} + +static void _hdr_phaser_set_epoch(int64_t* field, int64_t val) +{ + hdr_atomic_store_64(field, val); +} + +static int64_t _hdr_phaser_reset_epoch(int64_t* field, int64_t initial_value) +{ + return hdr_atomic_exchange_64(field, initial_value); +} + +int hdr_writer_reader_phaser_init(struct hdr_writer_reader_phaser* p) +{ + int rc; + if (NULL == p) + { + return EINVAL; + } + + p->start_epoch = 0; + p->even_end_epoch = 0; + p->odd_end_epoch = INT64_MIN; + p->reader_mutex = hdr_mutex_alloc(); + + if (!p->reader_mutex) + { + return ENOMEM; + } + + rc = hdr_mutex_init(p->reader_mutex); + if (0 != rc) + { + return rc; + } + + /* TODO: Should I fence here. */ + + return 0; +} + +void hdr_writer_reader_phaser_destroy(struct hdr_writer_reader_phaser* p) +{ + hdr_mutex_destroy(p->reader_mutex); + hdr_mutex_free(p->reader_mutex); +} + +int64_t hdr_phaser_writer_enter(struct hdr_writer_reader_phaser* p) +{ + return hdr_atomic_add_fetch_64(&p->start_epoch, 1); +} + +void hdr_phaser_writer_exit( + struct hdr_writer_reader_phaser* p, int64_t critical_value_at_enter) +{ + int64_t* end_epoch = + (critical_value_at_enter < 0) ? &p->odd_end_epoch : &p->even_end_epoch; + hdr_atomic_add_fetch_64(end_epoch, 1); +} + +void hdr_phaser_reader_lock(struct hdr_writer_reader_phaser* p) +{ + hdr_mutex_lock(p->reader_mutex); +} + +void hdr_phaser_reader_unlock(struct hdr_writer_reader_phaser* p) +{ + hdr_mutex_unlock(p->reader_mutex); +} + +void hdr_phaser_flip_phase( + struct hdr_writer_reader_phaser* p, int64_t sleep_time_ns) +{ + bool caught_up; + int64_t start_value_at_flip; + /* TODO: is_held_by_current_thread */ + unsigned int sleep_time_us = sleep_time_ns < 1000000000 ? (unsigned int) (sleep_time_ns / 1000) : 1000000; + + int64_t start_epoch = _hdr_phaser_get_epoch(&p->start_epoch); + + bool next_phase_is_even = (start_epoch < 0); + + /* Clear currently used phase end epoch.*/ + int64_t initial_start_value; + if (next_phase_is_even) + { + initial_start_value = 0; + _hdr_phaser_set_epoch(&p->even_end_epoch, initial_start_value); + } + else + { + initial_start_value = INT64_MIN; + _hdr_phaser_set_epoch(&p->odd_end_epoch, initial_start_value); + } + + /* Reset start value, indicating new phase.*/ + start_value_at_flip = _hdr_phaser_reset_epoch(&p->start_epoch, initial_start_value); + + do + { + int64_t* end_epoch = + next_phase_is_even ? &p->odd_end_epoch : &p->even_end_epoch; + + caught_up = _hdr_phaser_get_epoch(end_epoch) == start_value_at_flip; + + if (!caught_up) + { + if (sleep_time_us <= 0) + { + hdr_yield(); + } + else + { + hdr_usleep(sleep_time_us); + } + } + } + while (!caught_up); +} diff --git a/src/hdr_writer_reader_phaser.h b/src/hdr_writer_reader_phaser.h new file mode 100644 index 0000000..ded57ab --- /dev/null +++ b/src/hdr_writer_reader_phaser.h @@ -0,0 +1,51 @@ +/** + * hdr_writer_reader_phaser.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_WRITER_READER_PHASER_H +#define HDR_WRITER_READER_PHASER_H 1 + +#include +#include +#include +#include + +#include "hdr_thread.h" + +HDR_ALIGN_PREFIX(8) +struct hdr_writer_reader_phaser +{ + int64_t start_epoch; + int64_t even_end_epoch; + int64_t odd_end_epoch; + hdr_mutex* reader_mutex; +} +HDR_ALIGN_SUFFIX(8); + +#ifdef __cplusplus +extern "C" { +#endif + + int hdr_writer_reader_phaser_init(struct hdr_writer_reader_phaser* p); + + void hdr_writer_reader_phaser_destroy(struct hdr_writer_reader_phaser* p); + + int64_t hdr_phaser_writer_enter(struct hdr_writer_reader_phaser* p); + + void hdr_phaser_writer_exit( + struct hdr_writer_reader_phaser* p, int64_t critical_value_at_enter); + + void hdr_phaser_reader_lock(struct hdr_writer_reader_phaser* p); + + void hdr_phaser_reader_unlock(struct hdr_writer_reader_phaser* p); + + void hdr_phaser_flip_phase( + struct hdr_writer_reader_phaser* p, int64_t sleep_time_ns); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..726eaad --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,84 @@ +INCLUDE(CheckLibraryExists) +INCLUDE(ExternalProject) + +option(HDR_HISTOGRAM_BUILD_BENCHMARK "Build benchmark" OFF) + +if (HDR_HISTOGRAM_BUILD_BENCHMARK) + if (UNIX) + ExternalProject_Add( + google_benchmark + URL ${CMAKE_CURRENT_SOURCE_DIR}/../lib/benchmark-1.5.0.zip + URL_MD5 1af48846917b9730bee1848f6e38f563 + CMAKE_ARGS -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_ENABLE_INSTALL=OFF + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build" + INSTALL_COMMAND "" + ) + + ExternalProject_Get_Property(google_benchmark source_dir) + set(GOOGLE_BENCHMARK_SOURCE_DIR ${source_dir}) + ExternalProject_Get_Property(google_benchmark binary_dir) + set(GOOGLE_BENCHMARK_BINARY_DIR ${binary_dir}) + + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE) + add_executable(hdr_histogram_benchmark hdr_histogram_benchmark.cpp) + target_include_directories(hdr_histogram_benchmark PUBLIC ${GOOGLE_BENCHMARK_SOURCE_DIR}/include) + target_link_directories(hdr_histogram_benchmark PUBLIC ${GOOGLE_BENCHMARK_BINARY_DIR}/src) + target_link_libraries(hdr_histogram_benchmark benchmark) + target_link_libraries(hdr_histogram_benchmark hdr_histogram_static m z pthread) + else() + message(WARNING + "google.benchmark - microbenchmarks disabled on WIN32 platforms") + endif() +endif() + +add_executable(hdr_histogram_test hdr_histogram_test.c minunit.c) +add_executable(hdr_histogram_atomic_test hdr_histogram_atomic_test.c minunit.c) +add_executable(hdr_histogram_log_test hdr_histogram_log_test.c minunit.c) +add_executable(hdr_atomic_test hdr_atomic_test.c minunit.c hdr_test_util.h) + +add_executable(perftest hdr_histogram_perf.c) + +if (WIN32) + add_library(z STATIC IMPORTED) + set_property(TARGET z PROPERTY IMPORTED_LOCATION ${ZLIB_LIBRARIES}) + + target_link_libraries(hdr_histogram_test hdr_histogram_static ws2_32) + target_link_libraries(hdr_histogram_atomic_test hdr_histogram_static ws2_32) + target_link_libraries(hdr_histogram_log_test hdr_histogram_static z ws2_32) + target_link_libraries(perftest hdr_histogram_static z ws2_32) + target_link_libraries(hdr_atomic_test z ws2_32) +else() + target_link_libraries(hdr_histogram_test hdr_histogram_static m) + target_link_libraries(hdr_histogram_atomic_test hdr_histogram_static m) + target_link_libraries(hdr_histogram_log_test hdr_histogram_static m z) + target_link_libraries(perftest hdr_histogram_static m z) + target_link_libraries(hdr_atomic_test z) +endif() + +CHECK_LIBRARY_EXISTS(rt clock_gettime "" RT_EXISTS) +if (RT_EXISTS) + target_link_libraries(hdr_histogram_log_test rt) + target_link_libraries(perftest rt) +endif (RT_EXISTS) + +install(TARGETS hdr_histogram_test DESTINATION bin) +install(TARGETS hdr_histogram_atomic_test DESTINATION bin) +install(TARGETS hdr_histogram_log_test DESTINATION bin) +install(TARGETS perftest DESTINATION bin) + +add_test(Histogram hdr_histogram_test) +add_test(HistogramAtomic hdr_histogram_atomic_test) +add_test(HistogramLogging hdr_histogram_log_test) +add_test(Atomic hdr_atomic_test) + +if (UNIX) + add_executable(hdr_histogram_atomic_concurrency_test hdr_histogram_atomic_concurrency_test.c minunit.c) + target_link_libraries(hdr_histogram_atomic_concurrency_test hdr_histogram_static m pthread) + add_test(HistogramAtomicConcurrency hdr_histogram_atomic_concurrency_test) +endif() + +configure_file(jHiccup-2.0.1.logV0.hlog jHiccup-2.0.1.logV0.hlog COPYONLY) +configure_file(jHiccup-2.0.6.logV1.hlog jHiccup-2.0.6.logV1.hlog COPYONLY) +configure_file(jHiccup-2.0.7S.logV2.hlog jHiccup-2.0.7S.logV2.hlog COPYONLY) +configure_file(jHiccup-2.0.7S.logV3.hlog jHiccup-2.0.7S.logV3.hlog COPYONLY) diff --git a/test/hdr_atomic_test.c b/test/hdr_atomic_test.c new file mode 100644 index 0000000..cb6a6d5 --- /dev/null +++ b/test/hdr_atomic_test.c @@ -0,0 +1,104 @@ +/** + * hdr_histogram_test.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include + +#include +#include + +#include "minunit.h" + +int tests_run = 0; + +static char* test_store_load_64() +{ + int64_t value = 45; + int64_t b; + int64_t p = 0; + + hdr_atomic_store_64(&p, value); + mu_assert("Failed hdr_atomic_store_64", compare_int64(p, value)); + + b = hdr_atomic_load_64(&p); + mu_assert("Failed hdr_atomic_load_64", compare_int64(p, b)); + + return 0; +} + +static char* test_store_load_pointer() +{ + int64_t r = 12; + int64_t* q = 0; + int64_t* s; + + hdr_atomic_store_pointer((void**) &q, &r); + mu_assert("Failed hdr_atomic_store_pointer", compare_int64(*q, r)); + + s = hdr_atomic_load_pointer((void**) &q); + mu_assert("Failed hdr_atomic_load_pointer", compare_int64(*s, r)); + + return 0; +} + +static char* test_exchange() +{ + int64_t val1 = 123124; + int64_t val2 = 987234; + + int64_t p = val1; + int64_t q = val2; + + hdr_atomic_exchange_64(&p, q); + mu_assert("Failed hdr_atomic_exchange_64", compare_int64(p, val2)); + + return 0; +} + +static char* test_add() +{ + int64_t val1 = 123124; + int64_t val2 = 987234; + int64_t expected = val1 + val2; + + int64_t result = hdr_atomic_add_fetch_64(&val1, val2); + mu_assert("Failed hdr_atomic_exchange_64", compare_int64(result, expected)); + mu_assert("Failed hdr_atomic_exchange_64", compare_int64(val1, expected)); + + return 0; +} + +static struct mu_result all_tests() +{ + mu_run_test(test_store_load_64); + mu_run_test(test_store_load_pointer); + mu_run_test(test_exchange); + mu_run_test(test_add); + + mu_ok; +} + +static int hdr_atomic_run_tests() +{ + struct mu_result result = all_tests(); + + if (result.message != 0) + { + printf("hdr_atomic_test.%s(): %s\n", result.test, result.message); + } + else + { + printf("ALL TESTS PASSED\n"); + } + + printf("Tests run: %d\n", tests_run); + + return result.message == NULL ? 0 : -1; +} + +int main() +{ + return hdr_atomic_run_tests(); +} diff --git a/test/hdr_histogram_atomic_concurrency_test.c b/test/hdr_histogram_atomic_concurrency_test.c new file mode 100644 index 0000000..d18a881 --- /dev/null +++ b/test/hdr_histogram_atomic_concurrency_test.c @@ -0,0 +1,116 @@ +/** + * hdr_histogram_test.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "minunit.h" +#include "hdr_test_util.h" + +int tests_run = 0; + +struct test_histogram_data +{ + struct hdr_histogram* histogram; + int64_t* values; + int values_len; +}; + +static void* record_values(void* thread_context) +{ + int i; + struct test_histogram_data* thread_data = (struct test_histogram_data*) thread_context; + + + for (i = 0; i < thread_data->values_len; i++) + { + hdr_record_value_atomic(thread_data->histogram, thread_data->values[i]); + } + + pthread_exit(NULL); +} + + +static char* test_recording_concurrently() +{ + const int value_count = 10000000; + int64_t* values = calloc(value_count, sizeof(int64_t)); + struct hdr_histogram* expected_histogram; + struct hdr_histogram* actual_histogram; + struct test_histogram_data thread_data[2]; + struct hdr_iter expected_iter; + struct hdr_iter actual_iter; + pthread_t threads[2]; + int i; + + mu_assert("init", 0 == hdr_init(1, 10000000, 2, &expected_histogram)); + mu_assert("init", 0 == hdr_init(1, 10000000, 2, &actual_histogram)); + + + for (i = 0; i < value_count; i++) + { + values[i] = rand() % 20000; + } + + for (i = 0; i < value_count; i++) + { + hdr_record_value(expected_histogram, values[i]); + } + + thread_data[0].histogram = actual_histogram; + thread_data[0].values = values; + thread_data[0].values_len = value_count / 2; + pthread_create(&threads[0], NULL, record_values, &thread_data[0]); + + thread_data[1].histogram = actual_histogram; + thread_data[1].values = &values[value_count / 2]; + thread_data[1].values_len = value_count / 2; + pthread_create(&threads[1], NULL, record_values, &thread_data[1]); + + pthread_join(threads[0], NULL); + pthread_join(threads[1], NULL); + + hdr_iter_init(&expected_iter, expected_histogram); + hdr_iter_init(&actual_iter, actual_histogram); + + return compare_histograms(expected_histogram, actual_histogram); +} + +static struct mu_result all_tests() +{ + mu_run_test(test_recording_concurrently); + + mu_ok; +} + +static int hdr_histogram_run_tests() +{ + struct mu_result result = all_tests(); + + if (result.message != 0) + { + printf("hdr_histogram_test.%s(): %s\n", result.test, result.message); + } + else + { + printf("ALL TESTS PASSED\n"); + } + + printf("Tests run: %d\n", tests_run); + + return result.message == NULL ? 0 : -1; +} + +int main() +{ + return hdr_histogram_run_tests(); +} diff --git a/test/hdr_histogram_atomic_test.c b/test/hdr_histogram_atomic_test.c new file mode 100644 index 0000000..629ae39 --- /dev/null +++ b/test/hdr_histogram_atomic_test.c @@ -0,0 +1,567 @@ +/** + * hdr_histogram_test.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "minunit.h" +#include "hdr_test_util.h" + +static bool compare_values(double a, double b, double variation) +{ + return compare_double(a, b, b * variation); +} + +static bool compare_percentile(int64_t a, double b, double variation) +{ + return compare_values((double) a, b, variation); +} + + +int tests_run = 0; + +static struct hdr_histogram* raw_histogram = NULL; +static struct hdr_histogram* cor_histogram = NULL; +static struct hdr_histogram* scaled_raw_histogram = NULL; +static struct hdr_histogram* scaled_cor_histogram = NULL; + +static void load_histograms() +{ + const int64_t highest_trackable_value = INT64_C(3600) * 1000 * 1000; + const int32_t significant_figures = 3; + const int64_t interval = INT64_C(10000); + const int64_t scale = 512; + const int64_t scaled_interval = interval * scale; + + int i; + if (raw_histogram) + { + free(raw_histogram); + } + + hdr_init(1, highest_trackable_value, significant_figures, &raw_histogram); + + if (cor_histogram) + { + free(cor_histogram); + } + + hdr_init(1, highest_trackable_value, significant_figures, &cor_histogram); + + if (scaled_raw_histogram) + { + free(scaled_raw_histogram); + } + + hdr_init(1000, highest_trackable_value * 512, significant_figures, &scaled_raw_histogram); + + if (scaled_cor_histogram) + { + free(scaled_cor_histogram); + } + + hdr_init(1000, highest_trackable_value * 512, significant_figures, &scaled_cor_histogram); + + for (i = 0; i < 10000; i++) + { + hdr_record_value_atomic(raw_histogram, 1000); + hdr_record_corrected_value_atomic(cor_histogram, 1000, interval); + + hdr_record_value_atomic(scaled_raw_histogram, 1000 * scale); + hdr_record_corrected_value_atomic(scaled_cor_histogram, 1000 * scale, scaled_interval); + } + + hdr_record_value_atomic(raw_histogram, 100000000); + hdr_record_corrected_value_atomic(cor_histogram, 100000000, 10000L); + + hdr_record_value_atomic(scaled_raw_histogram, 100000000 * scale); + hdr_record_corrected_value_atomic(scaled_cor_histogram, 100000000 * scale, scaled_interval); +} + +static char* test_create() +{ + struct hdr_histogram* h = NULL; + int r = hdr_init(1, INT64_C(3600000000), 3, &h); + + mu_assert("Failed to allocate hdr_histogram", r == 0); + mu_assert("Failed to allocate hdr_histogram", h != NULL); + mu_assert("Incorrect array length", compare_int64(h->counts_len, 23552)); + + free(h); + + return 0; +} + +static char* test_create_with_large_values() +{ + struct hdr_histogram* h = NULL; + int r = hdr_init(20000000, 100000000, 5, &h); + mu_assert("Didn't create", r == 0); + + hdr_record_value_atomic(h, 100000000); + hdr_record_value_atomic(h, 20000000); + hdr_record_value_atomic(h, 30000000); + + mu_assert( + "50.0% Percentile", + hdr_values_are_equivalent(h, 20000000, hdr_value_at_percentile(h, 50.0))); + + mu_assert( + "83.33% Percentile", + hdr_values_are_equivalent(h, 30000000, hdr_value_at_percentile(h, 83.33))); + + mu_assert( + "83.34% Percentile", + hdr_values_are_equivalent(h, 100000000, hdr_value_at_percentile(h, 83.34))); + + mu_assert( + "99.0% Percentile", + hdr_values_are_equivalent(h, 100000000, hdr_value_at_percentile(h, 99.0))); + + return 0; +} + +static char* test_invalid_significant_figures() +{ + struct hdr_histogram* h = NULL; + + int r = hdr_alloc(36000000, -1, &h); + mu_assert("Result was not EINVAL", r == EINVAL); + mu_assert("Histogram was not null", h == 0); + + r = hdr_alloc(36000000, 6, &h); + mu_assert("Result was not EINVAL", r == EINVAL); + mu_assert("Histogram was not null", h == 0); + + return 0; +} + +static char* test_invalid_init() +{ + struct hdr_histogram* h = NULL; + + mu_assert("Should not allow 0 as lowest trackable value", EINVAL == hdr_init(0, 64*1024, 2, &h)); + mu_assert("Should have lowest < 2 * highest", EINVAL == hdr_init(80, 110, 5, &h)); + + return 0; +} + +static char* test_total_count() +{ + load_histograms(); + + mu_assert("Total raw count != 10001", raw_histogram->total_count == 10001); + mu_assert("Total corrected count != 20000", cor_histogram->total_count == 20000); + + return 0; +} + +static char* test_get_max_value() +{ + int64_t actual_raw_max, actual_cor_max; + + load_histograms(); + + actual_raw_max = hdr_max(raw_histogram); + mu_assert("hdr_max(raw_histogram) != 100000000L", + hdr_values_are_equivalent(raw_histogram, actual_raw_max, 100000000)); + actual_cor_max = hdr_max(cor_histogram); + mu_assert("hdr_max(cor_histogram) != 100000000L", + hdr_values_are_equivalent(cor_histogram, actual_cor_max, 100000000)); + + return 0; +} + +static char* test_get_min_value() +{ + load_histograms(); + + mu_assert("hdr_min(raw_histogram) != 1000", hdr_min(raw_histogram) == 1000); + mu_assert("hdr_min(cor_histogram) != 1000", hdr_min(cor_histogram) == 1000); + + return 0; +} + +static char* test_percentiles() +{ + load_histograms(); + + mu_assert("Value at 30% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 30.0), 1000.0, 0.001)); + mu_assert("Value at 99% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.0), 1000.0, 0.001)); + mu_assert("Value at 99.99% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.99), 1000.0, 0.001)); + mu_assert("Value at 99.999% not 100000000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.999), 100000000.0, 0.001)); + mu_assert("Value at 100% not 100000000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 100.0), 100000000.0, 0.001)); + + mu_assert("Value at 30% not 1000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 30.0), 1000.0, 0.001)); + mu_assert("Value at 50% not 1000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 50.0), 1000.0, 0.001)); + mu_assert("Value at 75% not 50000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 75.0), 50000000.0, 0.001)); + mu_assert("Value at 90% not 80000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 90.0), 80000000.0, 0.001)); + mu_assert("Value at 99% not 98000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 99.0), 98000000.0, 0.001)); + mu_assert("Value at 99.999% not 100000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 99.999), 100000000.0, 0.001)); + mu_assert("Value at 100% not 100000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 100.0), 100000000.0, 0.001)); + + return 0; +} + + +static char* test_recorded_values() +{ + struct hdr_iter iter; + int index; + int64_t total_added_count = 0; + + load_histograms(); + + /* Raw Histogram */ + hdr_iter_recorded_init(&iter, raw_histogram); + + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.recorded.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Value at 0 is not 10000", count_added_in_this_bucket == 10000); + } + else + { + mu_assert("Value at 1 is not 1", count_added_in_this_bucket == 1); + } + + index++; + } + mu_assert("Should have encountered 2 values", index == 2); + + /* Corrected Histogram */ + hdr_iter_recorded_init(&iter, cor_histogram); + + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.recorded.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Count at 0 is not 10000", count_added_in_this_bucket == 10000); + } + mu_assert("Count should not be 0", iter.count != 0); + mu_assert("Count at value iterated to should be count added in this step", + iter.count == count_added_in_this_bucket); + total_added_count += count_added_in_this_bucket; + index++; + } + mu_assert("Total counts should be 20000", total_added_count == 20000); + + return 0; +} + +static char* test_linear_values() +{ + struct hdr_iter iter; + int index; + int64_t total_added_count; + + load_histograms(); + + /* Raw Histogram */ + hdr_iter_linear_init(&iter, raw_histogram, 100000); + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.linear.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Count at 0 is not 10000", count_added_in_this_bucket == 10000); + } + else if (index == 999) + { + mu_assert("Count at 999 is not 1", count_added_in_this_bucket == 1); + } + else + { + mu_assert("Count should be 0", count_added_in_this_bucket == 0); + } + + index++; + } + mu_assert("Should of met 1000 values", compare_int64(index, 1000)); + + /* Corrected Histogram */ + + hdr_iter_linear_init(&iter, cor_histogram, 10000); + index = 0; + total_added_count = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.linear.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Count at 0 is not 10001", count_added_in_this_bucket == 10001); + } + + total_added_count += count_added_in_this_bucket; + index++; + } + mu_assert("Should of met 10001 values", index == 10000); + mu_assert("Should of met 20000 counts", total_added_count == 20000); + + return 0; +} + +static char* test_logarithmic_values() +{ + struct hdr_iter iter; + int index; + uint64_t total_added_count; + + load_histograms(); + + hdr_iter_log_init(&iter, raw_histogram, 10000, 2.0); + index = 0; + + while(hdr_iter_next(&iter)) + { + uint64_t count_added_in_this_bucket = iter.specifics.log.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Raw Logarithmic 10 msec bucket # 0 added a count of 10000", 10000 == count_added_in_this_bucket); + } + else if (index == 14) + { + mu_assert("Raw Logarithmic 10 msec bucket # 14 added a count of 1", 1 == count_added_in_this_bucket); + } + else + { + mu_assert("Raw Logarithmic 10 msec bucket added a count of 0", 0 == count_added_in_this_bucket); + } + + index++; + } + + mu_assert("Should of seen 14 values", index - 1 == 14); + + hdr_iter_log_init(&iter, cor_histogram, 10000, 2.0); + index = 0; + total_added_count = 0; + while (hdr_iter_next(&iter)) + { + uint64_t count_added_in_this_bucket = iter.specifics.log.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Corrected Logarithmic 10 msec bucket # 0 added a count of 10001", 10001 == count_added_in_this_bucket); + } + total_added_count += count_added_in_this_bucket; + index++; + } + + mu_assert("Should of seen 14 values", index - 1 == 14); + mu_assert("Should of seen count of 20000", total_added_count == 20000); + + return 0; +} + +static char* test_reset() +{ + load_histograms(); + + mu_assert("Value at 99% == 0.0", hdr_value_at_percentile(raw_histogram, 99.0) != 0); + mu_assert("Value at 99% == 0.0", hdr_value_at_percentile(cor_histogram, 99.0) != 0); + + hdr_reset(raw_histogram); + hdr_reset(cor_histogram); + + mu_assert("Total raw count != 0", raw_histogram->total_count == 0); + mu_assert("Total corrected count != 0", cor_histogram->total_count == 0); + + mu_assert("Value at 99% not 0.0", hdr_value_at_percentile(raw_histogram, 99.0) == 0); + mu_assert("Value at 99% not 0.0", hdr_value_at_percentile(cor_histogram, 99.0) == 0); + + return 0; +} + +static char* test_scaling_equivalence() +{ + int64_t expected_99th, scaled_99th; + load_histograms(); + + mu_assert( + "Averages should be equivalent", + compare_values( + hdr_mean(cor_histogram) * 512, + hdr_mean(scaled_cor_histogram), + 0.000001)); + + mu_assert( + "Total count should be equivalent", + compare_int64( + cor_histogram->total_count, + scaled_cor_histogram->total_count)); + + expected_99th = hdr_value_at_percentile(cor_histogram, 99.0) * 512; + scaled_99th = hdr_value_at_percentile(scaled_cor_histogram, 99.0); + mu_assert( + "99%'iles should be equivalent", + compare_int64( + hdr_lowest_equivalent_value(cor_histogram, expected_99th), + hdr_lowest_equivalent_value(scaled_cor_histogram, scaled_99th))); + + return 0; +} + +static char* test_out_of_range_values() +{ + struct hdr_histogram *h; + hdr_init(1, 1000, 4, &h); + mu_assert("Should successfully record value", hdr_record_value_atomic(h, 32767)); + mu_assert("Should not record value", !hdr_record_value_atomic(h, 32768)); + + return 0; +} + +static char* test_linear_iter_buckets_correctly() +{ + int step_count = 0; + int64_t total_count = 0; + struct hdr_histogram *h; + struct hdr_iter iter; + + hdr_init(1, 255, 2, &h); + + hdr_record_value_atomic(h, 193); + hdr_record_value_atomic(h, 255); + hdr_record_value_atomic(h, 0); + hdr_record_value_atomic(h, 1); + hdr_record_value_atomic(h, 64); + hdr_record_value_atomic(h, 128); + + hdr_iter_linear_init(&iter, h, 64); + + while (hdr_iter_next(&iter)) + { + total_count += iter.specifics.linear.count_added_in_this_iteration_step; + /* start - changes to reproduce issue */ + if (step_count == 0) + { + hdr_record_value_atomic(h, 2); + } + /* end - changes to reproduce issue */ + step_count++; + } + + mu_assert("Number of steps", compare_int64(4, step_count)); + mu_assert("Total count", compare_int64(6, total_count)); + + return 0; +} + +static char* test_interval_recording() +{ + int value_count, i, value; + char* result; + struct hdr_histogram* expected_histogram; + struct hdr_histogram* expected_corrected_histogram; + struct hdr_interval_recorder recorder; + struct hdr_interval_recorder recorder_corrected; + struct hdr_histogram* recorder_histogram; + struct hdr_histogram* recorder_corrected_histogram; + + value_count = 1000000; + hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3); + hdr_interval_recorder_init_all(&recorder_corrected, 1, INT64_C(24) * 60 * 60 * 1000000, 3); + hdr_init(1, INT64_C(24) * 60 * 60 * 1000000, 3, &expected_histogram); + hdr_init(1, INT64_C(24) * 60 * 60 * 1000000, 3, &expected_corrected_histogram); + + for (i = 0; i < value_count; i++) + { + value = rand() % 20000; + hdr_record_value(expected_histogram, value); + hdr_record_corrected_value(expected_corrected_histogram, value, 1000); + hdr_interval_recorder_record_value_atomic(&recorder, value); + hdr_interval_recorder_record_corrected_value_atomic(&recorder_corrected, value, 1000); + } + + recorder_histogram = hdr_interval_recorder_sample(&recorder); + + result = compare_histograms(expected_histogram, recorder_histogram); + if (result) + { + return result; + } + + recorder_corrected_histogram = hdr_interval_recorder_sample(&recorder_corrected); + result = compare_histograms(expected_corrected_histogram, recorder_corrected_histogram); + if (result) + { + return result; + } + + return 0; +} + +static struct mu_result all_tests() +{ + mu_run_test(test_create); + mu_run_test(test_invalid_init); + mu_run_test(test_create_with_large_values); + mu_run_test(test_invalid_significant_figures); + mu_run_test(test_total_count); + mu_run_test(test_get_min_value); + mu_run_test(test_get_max_value); + mu_run_test(test_percentiles); + mu_run_test(test_recorded_values); + mu_run_test(test_linear_values); + mu_run_test(test_logarithmic_values); + mu_run_test(test_reset); + mu_run_test(test_scaling_equivalence); + mu_run_test(test_out_of_range_values); + mu_run_test(test_linear_iter_buckets_correctly); + mu_run_test(test_interval_recording); + + mu_ok; +} + +static int hdr_histogram_run_tests() +{ + struct mu_result result = all_tests(); + + if (result.message != 0) + { + printf("hdr_histogram_test.%s(): %s\n", result.test, result.message); + } + else + { + printf("ALL TESTS PASSED\n"); + } + + printf("Tests run: %d\n", tests_run); + + return result.message == NULL ? 0 : -1; +} + +int main() +{ + return hdr_histogram_run_tests(); +} diff --git a/test/hdr_histogram_benchmark.cpp b/test/hdr_histogram_benchmark.cpp new file mode 100644 index 0000000..17bb90c --- /dev/null +++ b/test/hdr_histogram_benchmark.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#ifdef _WIN32 +#pragma comment ( lib, "Shlwapi.lib" ) +#ifdef _DEBUG +#pragma comment ( lib, "benchmarkd.lib" ) +#else +#pragma comment ( lib, "benchmark.lib" ) +#endif +#endif + +int64_t min_value = 1; +int64_t min_precision = 1; +int64_t max_precision = 4; +int64_t min_time_unit = 1000; +int64_t max_time_unit = 1000000; +int64_t step_time_unit = 1000; + +static void generate_arguments_pairs(benchmark::internal::Benchmark *b) +{ + for (int64_t precision = min_precision; precision <= max_precision; precision++) + { + for (int64_t time_unit = min_time_unit; time_unit <= max_time_unit; time_unit *= step_time_unit) + { + b = b->ArgPair(precision, INT64_C(24) * 60 * 60 * time_unit); + } + } +} + +static void BM_hdr_init(benchmark::State &state) +{ + const int64_t precision = state.range(0); + const int64_t max_value = state.range(1); + for (auto _ : state) + { + struct hdr_histogram *histogram; + benchmark::DoNotOptimize(hdr_init(min_value, max_value, precision, &histogram)); + // read/write barrier + benchmark::ClobberMemory(); + } +} + +static void BM_hdr_record_values(benchmark::State &state) +{ + const int64_t precision = state.range(0); + const int64_t max_value = state.range(1); + struct hdr_histogram *histogram; + hdr_init(min_value, max_value, precision, &histogram); + benchmark::DoNotOptimize(histogram->counts); + + for (auto _ : state) + { + benchmark::DoNotOptimize(hdr_record_values(histogram, 1000000, 1)); + // read/write barrier + benchmark::ClobberMemory(); + } +} + +// Register the functions as a benchmark +BENCHMARK(BM_hdr_init)->Apply(generate_arguments_pairs); +BENCHMARK(BM_hdr_record_values)->Apply(generate_arguments_pairs); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/test/hdr_histogram_log_test.c b/test/hdr_histogram_log_test.c new file mode 100644 index 0000000..61cc626 --- /dev/null +++ b/test/hdr_histogram_log_test.c @@ -0,0 +1,982 @@ +/** + * hdr_histogram_log_test.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hdr_time.h" +#include +#include +#include +#include "minunit.h" + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + +int tests_run = 0; + +static bool compare_int(int a, int b) +{ + if (a == b) + { + return true; + } + + printf("%d != %d\n", a, b); + return false; +} + +static long ns_to_ms(long ns) +{ + return (ns / 1000000) * 1000000; +} + +static bool compare_timespec(hdr_timespec* a, hdr_timespec* b) +{ + char a_str[128]; + char b_str[128]; + + long a_tv_msec = ns_to_ms(a->tv_nsec); + long b_tv_msec = ns_to_ms(b->tv_nsec); + + /* Allow off by 1 millisecond due to parsing and rounding. */ + if (a->tv_sec == b->tv_sec && labs(a_tv_msec - b_tv_msec) <= 1000000) + { + return true; + } + + if (a->tv_sec != b->tv_sec) + { +#if defined(_MSC_VER) + _ctime32_s(a_str, sizeof(a_str), &a->tv_sec); + _ctime32_s(b_str, sizeof(b_str), &b->tv_sec); + printf("tv_sec: %s != %s\n", a_str, b_str); +#else + printf( + "tv_sec: %s != %s\n", + ctime_r(&a->tv_sec, a_str), + ctime_r(&b->tv_sec, b_str)); +#endif + } + + if (a_tv_msec == b_tv_msec) + { + printf("%ld != %ld\n", a->tv_nsec, b->tv_nsec); + } + + return false; +} + +static bool compare_string(const char* a, const char* b, size_t len) +{ + if (strncmp(a, b, len) == 0) + { + return true; + } + + printf("%s != %s\n", a, b); + return false; +} + +static bool compare_histogram(struct hdr_histogram* a, struct hdr_histogram* b) +{ + int64_t a_max, b_max, a_min, b_min; + size_t a_size, b_size, counts_size; + struct hdr_iter iter_a, iter_b; + + if (a->counts_len != b->counts_len) + { + printf( + "a.counts_len = %"PRIu32", b.counts_len = %"PRIu32"\n", + a->counts_len, b->counts_len); + return false; + } + + a_max = hdr_max(a); + b_max = hdr_max(b); + + if (a_max != b_max) + { + printf("a.max = %"PRIu64", b.max = %"PRIu64"\n", a_max, b_max); + } + + a_min = hdr_min(a); + b_min = hdr_min(b); + + if (a_min != b_min) + { + printf("a.min = %"PRIu64", b.min = %"PRIu64"\n", a_min, b_min); + } + + a_size = hdr_get_memory_size(a); + b_size = hdr_get_memory_size(b); + + if (a_size != b_size) + { + printf("a.size: %u, b.size: %u\n", (unsigned) a_size, (unsigned) b_size); + return false; + } + + counts_size = a->counts_len * sizeof(int64_t); + + if (memcmp(a->counts, b->counts, counts_size) == 0) + { + return true; + } + + printf("%s\n", "Counts incorrect"); + + hdr_iter_init(&iter_a, a); + hdr_iter_init(&iter_b, b); + + while (hdr_iter_next(&iter_a) && hdr_iter_next(&iter_b)) + { + if (iter_a.count != iter_b.count || + iter_a.value != iter_b.value) + { + printf( + "A - value: %"PRIu64", count: %"PRIu64", B - value: %"PRIu64", count: %"PRIu64"\n", + iter_a.value, iter_a.count, + iter_b.value, iter_b.count); + } + } + + return false; +} + +static struct hdr_histogram* raw_histogram = NULL; +static struct hdr_histogram* cor_histogram = NULL; + +static void load_histograms() +{ + int i; + + free(raw_histogram); + free(cor_histogram); + + hdr_alloc(INT64_C(3600) * 1000 * 1000, 3, &raw_histogram); + hdr_alloc(INT64_C(3600) * 1000 * 1000, 3, &cor_histogram); + + for (i = 0; i < 10000; i++) + { + hdr_record_value(raw_histogram, 1000); + hdr_record_corrected_value(cor_histogram, 1000, 10000); + } + + hdr_record_value(raw_histogram, 100000000); + hdr_record_corrected_value(cor_histogram, 100000000, 10000); +} + +static bool validate_return_code(int rc) +{ + if (rc == 0) + { + return true; + } + + printf("%s\n", hdr_strerror(rc)); + return false; +} + +/* Prototypes to avoid exporting in header file. */ +void hdr_base64_encode_block(const uint8_t* input, char* output); + +void hdr_base64_decode_block(const char* input, uint8_t* output); +int hdr_encode_compressed(struct hdr_histogram* h, uint8_t** buffer, size_t* length); +int hdr_decode_compressed( + uint8_t* buffer, size_t length, struct hdr_histogram** histogram); +void hex_dump (char *desc, void *addr, int len); + +static char* test_encode_and_decode_compressed() +{ + uint8_t* buffer = NULL; + size_t len = 0; + int rc = 0; + struct hdr_histogram* actual = NULL; + struct hdr_histogram* expected; + + load_histograms(); + + expected = raw_histogram; + + rc = hdr_encode_compressed(expected, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + rc = hdr_decode_compressed(buffer, len, &actual); + mu_assert("Did not decode", validate_return_code(rc)); + + mu_assert("Loaded histogram is null", actual != NULL); + + mu_assert( + "Comparison did not match", + compare_histogram(expected, actual)); + + free(actual); + + return 0; +} + +static char* test_encode_and_decode_compressed2() +{ + uint8_t* buffer = NULL; + size_t len = 0; + int rc = 0; + struct hdr_histogram* actual = NULL; + struct hdr_histogram* expected; + + load_histograms(); + + expected = cor_histogram; + + rc = hdr_encode_compressed(expected, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + rc = hdr_decode_compressed(buffer, len, &actual); + mu_assert("Did not decode", validate_return_code(rc)); + + mu_assert("Loaded histogram is null", actual != NULL); + + mu_assert( + "Comparison did not match", + compare_histogram(expected, actual)); + + free(actual); + + return 0; +} + +static char* test_bounds_check_on_decode() +{ + uint8_t* buffer = NULL; + size_t len = 0; + int rc = 0; + struct hdr_histogram* actual = NULL; + struct hdr_histogram* expected; + + load_histograms(); + + expected = cor_histogram; + + rc = hdr_encode_compressed(expected, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + rc = hdr_decode_compressed(buffer, len - 1, &actual); + mu_assert("Should have be invalid", compare_int64(EINVAL, rc)); + mu_assert("Should not have built histogram", NULL == actual); + + return 0; +} + +static char* test_encode_and_decode_base64() +{ + uint8_t* buffer = NULL; + uint8_t* decoded = NULL; + char* encoded = NULL; + size_t encoded_len, decoded_len; + size_t len = 0; + int rc = 0; + + load_histograms(); + + rc = hdr_encode_compressed(cor_histogram, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + encoded_len = hdr_base64_encoded_len(len); + decoded_len = hdr_base64_decoded_len(encoded_len); + encoded = calloc(encoded_len + 1, sizeof(char)); + decoded = calloc(decoded_len, sizeof(uint8_t)); + + hdr_base64_encode(buffer, len, encoded, encoded_len); + hdr_base64_decode(encoded, encoded_len, decoded, decoded_len); + + mu_assert("Should be same", memcmp(buffer, decoded, len) == 0); + + return 0; +} + +static char* test_encode_and_decode_empty() +{ + uint8_t* buffer; + uint8_t* decoded; + char* encoded; + size_t len = 0; + int rc = 0; + size_t encoded_len; + size_t decoded_len; + + free(raw_histogram); + + mu_assert("allocation should be valid", 0 == hdr_init(1, 1000000, 1, &raw_histogram)); + + rc = hdr_encode_compressed(raw_histogram, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + encoded_len = hdr_base64_encoded_len(len); + decoded_len = hdr_base64_decoded_len(encoded_len); + encoded = calloc(encoded_len + 1, sizeof(char)); + decoded = calloc(decoded_len, sizeof(uint8_t)); + + hdr_base64_encode(buffer, len, encoded, encoded_len); + hdr_base64_decode(encoded, encoded_len, decoded, decoded_len); + + mu_assert("Should be same", memcmp(buffer, decoded, len) == 0); + + return 0; +} + +static char* test_encode_and_decode_compressed_large() +{ + const int64_t limit = INT64_C(3600) * 1000 * 1000; + struct hdr_histogram* actual = NULL; + struct hdr_histogram* expected = NULL; + uint8_t* buffer = NULL; + size_t len = 0; + int i; + int rc = 0; + + hdr_init(1, limit, 4, &expected); + srand(5); + + for (i = 0; i < 8070; i++) + { + hdr_record_value(expected, rand() % limit); + } + + rc = hdr_encode_compressed(expected, &buffer, &len); + mu_assert("Did not encode", validate_return_code(rc)); + + rc = hdr_decode_compressed(buffer, len, &actual); + mu_assert("Did not decode", validate_return_code(rc)); + + mu_assert("Loaded histogram is null", actual != NULL); + + mu_assert( + "Comparison did not match", + compare_histogram(expected, actual)); + + free(expected); + free(actual); + + return 0; +} + + +static bool assert_base64_encode(const char* input, const char* expected) +{ + size_t input_len = strlen(input); + int output_len = (int) (ceil(input_len / 3.0) * 4.0); + + char* output = calloc(sizeof(char), output_len); + + int r = hdr_base64_encode((uint8_t*)input, input_len, output, output_len); + bool result = r == 0 && compare_string(expected, output, output_len); + + free(output); + + return result; +} + +static char* base64_encode_encodes_without_padding() +{ + mu_assert( + "Encoding without padding", + assert_base64_encode( + "any carnal pleasur", + "YW55IGNhcm5hbCBwbGVhc3Vy")); + + return 0; +} + +static char* base64_encode_encodes_with_padding() +{ + mu_assert( + "Encoding with padding '='", + assert_base64_encode( + "any carnal pleasure.", + "YW55IGNhcm5hbCBwbGVhc3VyZS4=")); + mu_assert( + "Encoding with padding '=='", + assert_base64_encode( + "any carnal pleasure", + "YW55IGNhcm5hbCBwbGVhc3VyZQ==")); + + return 0; +} + +static char* base64_encode_fails_with_invalid_lengths() +{ + mu_assert( + "Output length not 4/3 of input length", + hdr_base64_encode(NULL, 9, NULL, 11)); + + return 0; +} + +static char* base64_encode_block_encodes_3_bytes() +{ + char output[5] = { 0 }; + + hdr_base64_encode_block((uint8_t*)"Man", output); + mu_assert("Encoding", compare_string("TWFu", output, 4)); + + return 0; +} + +static char* base64_decode_block_decodes_4_chars() +{ + uint8_t output[4] = { 0 }; + + hdr_base64_decode_block("TWFu", output); + mu_assert("Decoding", compare_string("Man", (char*) output, 3)); + + return 0; +} + +static bool assert_base64_decode(const char* base64_encoded, const char* expected) +{ + size_t encoded_len = strlen(base64_encoded); + size_t output_len = (encoded_len / 4) * 3; + + uint8_t* output = calloc(sizeof(uint8_t), output_len); + + int result = hdr_base64_decode(base64_encoded, encoded_len, output, output_len); + + return result == 0 && compare_string(expected, (char*)output, output_len); +} + +static char* base64_decode_decodes_strings_without_padding() +{ + mu_assert( + "Encoding without padding", + assert_base64_decode( + "YW55IGNhcm5hbCBwbGVhc3Vy", + "any carnal pleasur")); + + return 0; +} + +static char* base64_decode_decodes_strings_with_padding() +{ + mu_assert( + "Encoding with padding '='", + assert_base64_decode( + "YW55IGNhcm5hbCBwbGVhc3VyZS4=", + "any carnal pleasure.")); + + mu_assert( + "Encoding with padding '=='", + assert_base64_decode( + "YW55IGNhcm5hbCBwbGVhc3VyZQ==", + "any carnal pleasure")); + + return 0; +} + +static char* base64_decode_fails_with_invalid_lengths() +{ + mu_assert("Input length % 4 != 0", hdr_base64_decode(NULL, 5, NULL, 3) != 0); + mu_assert("Input length < 4", hdr_base64_decode(NULL, 3, NULL, 3) != 0); + mu_assert( + "Output length not 3/4 of input length", + hdr_base64_decode(NULL, 8, NULL, 7) != 0); + + return 0; +} + +static char* writes_and_reads_log() +{ + struct hdr_log_writer writer; + struct hdr_log_reader reader; + struct hdr_histogram* read_cor_histogram; + struct hdr_histogram* read_raw_histogram; + const char* file_name = "histogram.log"; + hdr_timespec timestamp; + hdr_timespec interval; + int rc = 0; + FILE* log_file; + hdr_timespec actual_timestamp, actual_interval; + + hdr_gettime(×tamp); + + interval.tv_sec = 5; + interval.tv_nsec = 2000000; + + hdr_log_writer_init(&writer); + hdr_log_reader_init(&reader); + + log_file = fopen(file_name, "w+"); + + rc = hdr_log_write_header(&writer, log_file, "Test log", ×tamp); + mu_assert("Failed header write", validate_return_code(rc)); + hdr_log_write(&writer, log_file, ×tamp, &interval, cor_histogram); + mu_assert("Failed corrected write", validate_return_code(rc)); + hdr_log_write(&writer, log_file, ×tamp, &interval, raw_histogram); + mu_assert("Failed raw write", validate_return_code(rc)); + + fprintf(log_file, "\n"); + + fflush(log_file); + fclose(log_file); + + log_file = fopen(file_name, "r"); + + read_cor_histogram = NULL; + read_raw_histogram = NULL; + + rc = hdr_log_read_header(&reader, log_file); + mu_assert("Failed header read", validate_return_code(rc)); + mu_assert("Incorrect major version", compare_int(reader.major_version, 1)); + mu_assert("Incorrect minor version", compare_int(reader.minor_version, 2)); + mu_assert( + "Incorrect start timestamp", + compare_timespec(&reader.start_timestamp, ×tamp)); + + + rc = hdr_log_read( + &reader, log_file, &read_cor_histogram, + &actual_timestamp, &actual_interval); + mu_assert("Failed corrected read", validate_return_code(rc)); + mu_assert( + "Incorrect first timestamp", compare_timespec(&actual_timestamp, ×tamp)); + mu_assert( + "Incorrect first interval", compare_timespec(&actual_interval, &interval)); + + rc = hdr_log_read(&reader, log_file, &read_raw_histogram, NULL, NULL); + mu_assert("Failed raw read", validate_return_code(rc)); + + mu_assert( + "Histograms do not match", + compare_histogram(cor_histogram, read_cor_histogram)); + + mu_assert( + "Histograms do not match", + compare_histogram(raw_histogram, read_raw_histogram)); + + rc = hdr_log_read(&reader, log_file, &read_cor_histogram, NULL, NULL); + mu_assert("No EOF at end of file", rc == EOF); + + fclose(log_file); + remove(file_name); + + return 0; +} + +static char* log_reader_aggregates_into_single_histogram() +{ + const char* file_name = "histogram.log"; + hdr_timespec timestamp; + hdr_timespec interval; + struct hdr_log_writer writer; + struct hdr_log_reader reader; + int rc = 0; + FILE* log_file; + struct hdr_histogram* histogram; + struct hdr_iter iter; + int64_t expected_total_count; + + hdr_gettime(×tamp); + interval.tv_sec = 5; + interval.tv_nsec = 2000000; + + hdr_log_writer_init(&writer); + hdr_log_reader_init(&reader); + + log_file = fopen(file_name, "w+"); + + hdr_log_write_header(&writer, log_file, "Test log", ×tamp); + hdr_log_write(&writer, log_file, ×tamp, &interval, cor_histogram); + hdr_log_write(&writer, log_file, ×tamp, &interval, raw_histogram); + fflush(log_file); + fclose(log_file); + + log_file = fopen(file_name, "r"); + + hdr_alloc(INT64_C(3600) * 1000 * 1000, 3, &histogram); + + rc = hdr_log_read_header(&reader, log_file); + mu_assert("Failed header read", validate_return_code(rc)); + rc = hdr_log_read(&reader, log_file, &histogram, NULL, NULL); + mu_assert("Failed corrected read", validate_return_code(rc)); + rc = hdr_log_read(&reader, log_file, &histogram, NULL, NULL); + mu_assert("Failed raw read", validate_return_code(rc)); + + hdr_iter_recorded_init(&iter, histogram); + expected_total_count = raw_histogram->total_count + cor_histogram->total_count; + + mu_assert( + "Total counts incorrect", + compare_int64(histogram->total_count, expected_total_count)); + + while (hdr_iter_next(&iter)) + { + int64_t count = iter.count; + int64_t value = iter.value; + + int64_t expected_count = + hdr_count_at_value(raw_histogram, value) + + hdr_count_at_value(cor_histogram, value); + + mu_assert("Incorrect count", compare_int64(count, expected_count)); + } + + fclose(log_file); + remove(file_name); + free(histogram); + + return 0; +} + +static char* log_reader_fails_with_incorrect_version() +{ + const char* log_with_invalid_version = + "#[Test log]\n" + "#[Histogram log format version 1.04]\n" + "#[StartTime: 1404700005.222 (seconds since epoch), Mon Jul 02:26:45 GMT 2014]\n" + "StartTimestamp\",\"EndTimestamp\",\"Interval_Max\",\"Interval_Compressed_Histogram\"\n"; + const char* file_name = "histogram_with_invalid_version.log"; + struct hdr_log_reader reader; + FILE* log_file; + int r; + + log_file = fopen(file_name, "w+"); + fprintf(log_file, "%s", log_with_invalid_version); + fflush(log_file); + fclose(log_file); + + log_file = fopen(file_name, "r"); + hdr_log_reader_init(&reader); + r = hdr_log_read_header(&reader, log_file); + + mu_assert("Should error with incorrect version", r == HDR_LOG_INVALID_VERSION); + + fclose(log_file); + remove(file_name); + + return 0; +} + +static char* test_encode_decode_empty() +{ + char *data; + struct hdr_histogram *histogram, *hdr_new = NULL; + + hdr_alloc(INT64_C(3600) * 1000 * 1000, 3, &histogram); + + mu_assert("Failed to encode histogram data", hdr_log_encode(histogram, &data) == 0); + mu_assert("Failed to decode histogram data", hdr_log_decode(&hdr_new, data, strlen(data)) == 0); + mu_assert("Histograms should be the same", compare_histogram(histogram, hdr_new)); + free(histogram); + free(hdr_new); + free(data); + return 0; +} + +static char* test_string_encode_decode() +{ + int i; + char *data; + struct hdr_histogram *histogram, *hdr_new = NULL; + + hdr_alloc(INT64_C(3600) * 1000 * 1000, 3, &histogram); + + for (i = 1; i < 100; i++) + { + hdr_record_value(histogram, i*i); + } + + mu_assert("Failed to encode histogram data", hdr_log_encode(histogram, &data) == 0); + mu_assert("Failed to decode histogram data", hdr_log_decode(&hdr_new, data, strlen(data)) == 0); + mu_assert("Histograms should be the same", compare_histogram(histogram, hdr_new)); + mu_assert("Mean different after encode/decode", compare_double(hdr_mean(histogram), hdr_mean(hdr_new), 0.001)); + + return 0; +} + +static char* test_string_encode_decode_2() +{ + int i; + char *data; + + struct hdr_histogram *histogram, *hdr_new = NULL; + + hdr_alloc(1000, 3, &histogram); + + for (i = 1; i < histogram->highest_trackable_value; i++) + { + hdr_record_value(histogram, i); + } + + mu_assert( + "Failed to encode histogram data", validate_return_code(hdr_log_encode(histogram, &data))); + mu_assert( + "Failed to decode histogram data", validate_return_code(hdr_log_decode(&hdr_new, data, strlen(data)))); + mu_assert("Histograms should be the same", compare_histogram(histogram, hdr_new)); + mu_assert("Mean different after encode/decode", compare_double(hdr_mean(histogram), hdr_mean(hdr_new), 0.001)); + + return 0; +} + + +static char* decode_v1_log() +{ + const char* v1_log = "jHiccup-2.0.6.logV1.hlog"; + struct hdr_histogram* accum; + struct hdr_histogram* h = NULL; + struct hdr_log_reader reader; + hdr_timespec timestamp, interval; + int rc; + int64_t total_count = 0; + int histogram_count = 0; + + FILE* f = fopen(v1_log, "r"); + mu_assert("Can not open v1 log file", f != NULL); + + hdr_init(1, INT64_C(3600000000000), 3, &accum); + + + hdr_log_reader_init(&reader); + + rc = hdr_log_read_header(&reader, f); + mu_assert("Failed to read header", rc == 0); + + while ((rc = hdr_log_read(&reader, f, &h, ×tamp, &interval)) != EOF) + { + int64_t dropped; + + mu_assert("Failed to read histogram", rc == 0); + histogram_count++; + total_count += h->total_count; + dropped = hdr_add(accum, h); + mu_assert("Dropped events", compare_int64(dropped, 0)); + + free(h); + h = NULL; + } + + mu_assert("Wrong number of histograms", compare_int(histogram_count, 88)); + mu_assert("Wrong total count", compare_int64(total_count, 65964)); + mu_assert("99.9 percentile wrong", compare_int64(1829765119, hdr_value_at_percentile(accum, 99.9))); + mu_assert("max value wrong", compare_int64(1888485375, hdr_max(accum))); + mu_assert("Seconds wrong", compare_int64(1438867590, reader.start_timestamp.tv_sec)); + mu_assert("Nanoseconds wrong", compare_int64(285000000, reader.start_timestamp.tv_nsec)); + + return 0; +} + + +static char* decode_v2_log() +{ + struct hdr_histogram* accum; + struct hdr_histogram* h = NULL; + struct hdr_log_reader reader; + hdr_timespec timestamp, interval; + int histogram_count = 0; + int64_t total_count = 0; + int rc; + + const char* v2_log = "jHiccup-2.0.7S.logV2.hlog"; + + FILE* f = fopen(v2_log, "r"); + mu_assert("Can not open v2 log file", f != NULL); + + hdr_init(1, INT64_C(3600000000000), 3, &accum); + + hdr_log_reader_init(&reader); + + rc = hdr_log_read_header(&reader, f); + mu_assert("Failed to read header", validate_return_code(rc)); + + while ((rc = hdr_log_read(&reader, f, &h, ×tamp, &interval)) != EOF) + { + int64_t dropped; + + mu_assert("Failed to read histogram", validate_return_code(rc)); + histogram_count++; + total_count += h->total_count; + dropped = hdr_add(accum, h); + mu_assert("Dropped events", compare_int64(dropped, 0)); + + free(h); + h = NULL; + } + + mu_assert("Wrong number of histograms", compare_int(histogram_count, 62)); + mu_assert("Wrong total count", compare_int64(total_count, 48761)); + mu_assert("99.9 percentile wrong", compare_int64(1745879039, hdr_value_at_percentile(accum, 99.9))); + mu_assert("max value wrong", compare_int64(1796210687, hdr_max(accum))); + mu_assert("Seconds wrong", compare_int64(1441812279, reader.start_timestamp.tv_sec)); + mu_assert("Nanoseconds wrong", compare_int64(474000000, reader.start_timestamp.tv_nsec)); + + return 0; +} + +static char* decode_v3_log() +{ + struct hdr_histogram* accum; + struct hdr_histogram* h = NULL; + struct hdr_log_reader reader; + hdr_timespec timestamp; + hdr_timespec interval; + int rc; + int histogram_count = 0; + int64_t total_count = 0; + + const char* v3_log = "jHiccup-2.0.7S.logV3.hlog"; + + FILE* f = fopen(v3_log, "r"); + mu_assert("Can not open v3 log file", f != NULL); + + hdr_init(1, INT64_C(3600000000000), 3, &accum); + + hdr_log_reader_init(&reader); + + rc = hdr_log_read_header(&reader, f); + mu_assert("Failed to read header", validate_return_code(rc)); + + while ((rc = hdr_log_read(&reader, f, &h, ×tamp, &interval)) != EOF) + { + int64_t dropped; + mu_assert("Failed to read histogram", validate_return_code(rc)); + histogram_count++; + total_count += h->total_count; + dropped = hdr_add(accum, h); + mu_assert("Dropped events", compare_int64(dropped, 0)); + + free(h); + h = NULL; + } + + mu_assert("Wrong number of histograms", compare_int(histogram_count, 62)); + mu_assert("Wrong total count", compare_int64(total_count, 48761)); + mu_assert("99.9 percentile wrong", compare_int64(1745879039, hdr_value_at_percentile(accum, 99.9))); + mu_assert("max value wrong", compare_int64(1796210687, hdr_max(accum))); + mu_assert("Seconds wrong", compare_int64(1441812279, reader.start_timestamp.tv_sec)); + mu_assert("Nanoseconds wrong", compare_int64(474000000, reader.start_timestamp.tv_nsec)); + + return 0; +} + +static char* decode_v0_log() +{ + struct hdr_histogram* accum; + const char* v1_log = "jHiccup-2.0.1.logV0.hlog"; + struct hdr_histogram* h = NULL; + struct hdr_log_reader reader; + hdr_timespec timestamp; + hdr_timespec interval; + int rc; + int histogram_count = 0; + int64_t total_count = 0; + + FILE* f = fopen(v1_log, "r"); + mu_assert("Can not open v1 log file", f != NULL); + + hdr_init(1, INT64_C(3600000000000), 3, &accum); + + hdr_log_reader_init(&reader); + + rc = hdr_log_read_header(&reader, f); + mu_assert("Failed to read header", rc == 0); + + while ((rc = hdr_log_read(&reader, f, &h, ×tamp, &interval)) != EOF) + { + int64_t dropped; + mu_assert("Failed to read histogram", rc == 0); + histogram_count++; + total_count += h->total_count; + dropped = hdr_add(accum, h); + mu_assert("Dropped events", compare_int64(dropped, 0)); + + free(h); + h = NULL; + } + + mu_assert("Wrong number of histograms", compare_int(histogram_count, 81)); + mu_assert("Wrong total count", compare_int64(total_count, 61256)); + mu_assert("99.9 percentile wrong", compare_int64(1510998015, hdr_value_at_percentile(accum, 99.9))); + mu_assert("max value wrong", compare_int64(1569718271, hdr_max(accum))); + mu_assert("Seconds wrong", compare_int64(1438869961, reader.start_timestamp.tv_sec)); + mu_assert("Nanoseconds wrong", compare_int64(225000000, reader.start_timestamp.tv_nsec)); + + return 0; +} + +static struct mu_result all_tests() +{ + tests_run = 0; + + mu_run_test(test_encode_decode_empty); + mu_run_test(test_encode_and_decode_compressed); + mu_run_test(test_encode_and_decode_compressed2); + mu_run_test(test_encode_and_decode_compressed_large); + mu_run_test(test_encode_and_decode_base64); + mu_run_test(test_bounds_check_on_decode); + + mu_run_test(base64_decode_block_decodes_4_chars); + mu_run_test(base64_decode_fails_with_invalid_lengths); + mu_run_test(base64_decode_decodes_strings_without_padding); + mu_run_test(base64_decode_decodes_strings_with_padding); + + mu_run_test(base64_encode_block_encodes_3_bytes); + mu_run_test(base64_encode_fails_with_invalid_lengths); + mu_run_test(base64_encode_encodes_without_padding); + mu_run_test(base64_encode_encodes_with_padding); + + mu_run_test(writes_and_reads_log); + mu_run_test(log_reader_aggregates_into_single_histogram); + mu_run_test(log_reader_fails_with_incorrect_version); + + mu_run_test(test_string_encode_decode); + mu_run_test(test_string_encode_decode_2); + + mu_run_test(decode_v3_log); + mu_run_test(decode_v2_log); + mu_run_test(decode_v1_log); + mu_run_test(decode_v0_log); + + mu_run_test(test_encode_and_decode_empty); + + free(raw_histogram); + free(cor_histogram); + + mu_ok; +} + +static int hdr_histogram_log_run_tests() +{ + struct mu_result result = all_tests(); + + if (result.message != 0) + { + printf("hdr_histogram_log_test.%s(): %s\n", result.test, result.message); + } + else + { + printf("ALL TESTS PASSED\n"); + } + + printf("Tests run: %d\n", tests_run); + + return result.message == NULL ? 0 : -1; +} + +int main() +{ + return hdr_histogram_log_run_tests(); +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/test/hdr_histogram_perf.c b/test/hdr_histogram_perf.c new file mode 100644 index 0000000..1721402 --- /dev/null +++ b/test/hdr_histogram_perf.c @@ -0,0 +1,104 @@ +/** + * hdr_histogram_perf.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include + +#include +#include +#include + +#include "hdr_time.h" + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#define snprintf sprintf_s + +#endif + + +static hdr_timespec diff(hdr_timespec start, hdr_timespec end) +{ + hdr_timespec temp; + if ((end.tv_nsec-start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec-start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +/* Formats the given double with 2 dps, and , thousand separators */ +static char *format_double(double d) +{ + int p; + static char buffer[30]; + + snprintf(buffer, sizeof(buffer), "%0.2f", d); + + p = (int) strlen(buffer) - 6; + + while (p > 0) + { + memmove(&buffer[p + 1], &buffer[p], strlen(buffer) - p + 1); + buffer[p] = ','; + + p = p - 3; + } + + return buffer; +} + +int main() +{ + struct hdr_histogram* histogram; + hdr_timespec t0, t1; + int result, i; + int64_t iterations; + int64_t max_value = INT64_C(24) * 60 * 60 * 1000000; + int64_t min_value = 1; + + result = hdr_init(min_value, max_value, 4, &histogram); + if (result != 0) + { + fprintf(stderr, "Failed to allocate histogram: %d\n", result); + return -1; + } + + iterations = 400000000; + + for (i = 0; i < 100; i++) + { + int64_t j; + hdr_timespec taken; + double time_taken, ops_sec; + + hdr_gettime(&t0); + for (j = 1; j < iterations; j++) + { + hdr_record_value(histogram, j); + } + hdr_gettime(&t1); + + taken = diff(t0, t1); + time_taken = taken.tv_sec + taken.tv_nsec / 1000000000.0; + ops_sec = (iterations - 1) / time_taken; + + printf("%s - %d, ops/sec: %s\n", "Iteration", i + 1, format_double(ops_sec)); + } + + return 0; +} diff --git a/test/hdr_histogram_test.c b/test/hdr_histogram_test.c new file mode 100644 index 0000000..7ed69ba --- /dev/null +++ b/test/hdr_histogram_test.c @@ -0,0 +1,593 @@ +/** + * hdr_histogram_test.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "minunit.h" +#include "hdr_test_util.h" + +static bool compare_values(double a, double b, double variation) +{ + return compare_double(a, b, b * variation); +} + +static bool compare_percentile(int64_t a, double b, double variation) +{ + return compare_values((double) a, b, variation); +} + + +int tests_run = 0; + +static struct hdr_histogram* raw_histogram = NULL; +static struct hdr_histogram* cor_histogram = NULL; +static struct hdr_histogram* scaled_raw_histogram = NULL; +static struct hdr_histogram* scaled_cor_histogram = NULL; + +static void load_histograms() +{ + const int64_t highest_trackable_value = INT64_C(3600) * 1000 * 1000; + const int32_t significant_figures = 3; + const int64_t interval = INT64_C(10000); + const int64_t scale = 512; + const int64_t scaled_interval = interval * scale; + + int i; + if (raw_histogram) + { + free(raw_histogram); + } + + hdr_init(1, highest_trackable_value, significant_figures, &raw_histogram); + + if (cor_histogram) + { + free(cor_histogram); + } + + hdr_init(1, highest_trackable_value, significant_figures, &cor_histogram); + + if (scaled_raw_histogram) + { + free(scaled_raw_histogram); + } + + hdr_init(1000, highest_trackable_value * 512, significant_figures, &scaled_raw_histogram); + + if (scaled_cor_histogram) + { + free(scaled_cor_histogram); + } + + hdr_init(1000, highest_trackable_value * 512, significant_figures, &scaled_cor_histogram); + + for (i = 0; i < 10000; i++) + { + hdr_record_value(raw_histogram, 1000); + hdr_record_corrected_value(cor_histogram, 1000, interval); + + hdr_record_value(scaled_raw_histogram, 1000 * scale); + hdr_record_corrected_value(scaled_cor_histogram, 1000 * scale, scaled_interval); + } + + hdr_record_value(raw_histogram, 100000000); + hdr_record_corrected_value(cor_histogram, 100000000, 10000L); + + hdr_record_value(scaled_raw_histogram, 100000000 * scale); + hdr_record_corrected_value(scaled_cor_histogram, 100000000 * scale, scaled_interval); +} + +static char* test_create() +{ + struct hdr_histogram* h = NULL; + int r = hdr_init(1, INT64_C(3600000000), 3, &h); + + mu_assert("Failed to allocate hdr_histogram", r == 0); + mu_assert("Failed to allocate hdr_histogram", h != NULL); + mu_assert("Incorrect array length", compare_int64(h->counts_len, 23552)); + + free(h); + + return 0; +} + +static char* test_create_with_large_values() +{ + struct hdr_histogram* h = NULL; + int r = hdr_init(20000000, 100000000, 5, &h); + mu_assert("Didn't create", r == 0); + + hdr_record_value(h, 100000000); + hdr_record_value(h, 20000000); + hdr_record_value(h, 30000000); + + mu_assert( + "50.0% Percentile", + hdr_values_are_equivalent(h, 20000000, hdr_value_at_percentile(h, 50.0))); + + mu_assert( + "83.33% Percentile", + hdr_values_are_equivalent(h, 30000000, hdr_value_at_percentile(h, 83.33))); + + mu_assert( + "83.34% Percentile", + hdr_values_are_equivalent(h, 100000000, hdr_value_at_percentile(h, 83.34))); + + mu_assert( + "99.0% Percentile", + hdr_values_are_equivalent(h, 100000000, hdr_value_at_percentile(h, 99.0))); + + return 0; +} + +static char* test_invalid_significant_figures() +{ + struct hdr_histogram* h = NULL; + + int r = hdr_alloc(36000000, -1, &h); + mu_assert("Result was not EINVAL", r == EINVAL); + mu_assert("Histogram was not null", h == 0); + + r = hdr_alloc(36000000, 6, &h); + mu_assert("Result was not EINVAL", r == EINVAL); + mu_assert("Histogram was not null", h == 0); + + return 0; +} + +static char* test_invalid_init() +{ + struct hdr_histogram* h = NULL; + + mu_assert("Should not allow 0 as lowest trackable value", EINVAL == hdr_init(0, 64*1024, 2, &h)); + mu_assert("Should have lowest < 2 * highest", EINVAL == hdr_init(80, 110, 5, &h)); + + return 0; +} + +static char* test_total_count() +{ + load_histograms(); + + mu_assert("Total raw count != 10001", raw_histogram->total_count == 10001); + mu_assert("Total corrected count != 20000", cor_histogram->total_count == 20000); + + return 0; +} + +static char* test_get_max_value() +{ + int64_t actual_raw_max, actual_cor_max; + + load_histograms(); + + actual_raw_max = hdr_max(raw_histogram); + mu_assert("hdr_max(raw_histogram) != 100000000L", + hdr_values_are_equivalent(raw_histogram, actual_raw_max, 100000000)); + actual_cor_max = hdr_max(cor_histogram); + mu_assert("hdr_max(cor_histogram) != 100000000L", + hdr_values_are_equivalent(cor_histogram, actual_cor_max, 100000000)); + + return 0; +} + +static char* test_get_min_value() +{ + load_histograms(); + + mu_assert("hdr_min(raw_histogram) != 1000", hdr_min(raw_histogram) == 1000); + mu_assert("hdr_min(cor_histogram) != 1000", hdr_min(cor_histogram) == 1000); + + return 0; +} + +static char* test_percentiles() +{ + load_histograms(); + + mu_assert("Value at 30% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 30.0), 1000.0, 0.001)); + mu_assert("Value at 99% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.0), 1000.0, 0.001)); + mu_assert("Value at 99.99% not 1000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.99), 1000.0, 0.001)); + mu_assert("Value at 99.999% not 100000000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 99.999), 100000000.0, 0.001)); + mu_assert("Value at 100% not 100000000.0", + compare_percentile(hdr_value_at_percentile(raw_histogram, 100.0), 100000000.0, 0.001)); + + mu_assert("Value at 30% not 1000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 30.0), 1000.0, 0.001)); + mu_assert("Value at 50% not 1000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 50.0), 1000.0, 0.001)); + mu_assert("Value at 75% not 50000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 75.0), 50000000.0, 0.001)); + mu_assert("Value at 90% not 80000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 90.0), 80000000.0, 0.001)); + mu_assert("Value at 99% not 98000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 99.0), 98000000.0, 0.001)); + mu_assert("Value at 99.999% not 100000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 99.999), 100000000.0, 0.001)); + mu_assert("Value at 100% not 100000000.0", + compare_percentile(hdr_value_at_percentile(cor_histogram, 100.0), 100000000.0, 0.001)); + + return 0; +} + + +static char* test_recorded_values() +{ + struct hdr_iter iter; + int index; + int64_t total_added_count = 0; + + load_histograms(); + + /* Raw Histogram */ + hdr_iter_recorded_init(&iter, raw_histogram); + + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.recorded.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Value at 0 is not 10000", count_added_in_this_bucket == 10000); + } + else + { + mu_assert("Value at 1 is not 1", count_added_in_this_bucket == 1); + } + + index++; + } + mu_assert("Should have encountered 2 values", index == 2); + + /* Corrected Histogram */ + hdr_iter_recorded_init(&iter, cor_histogram); + + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.recorded.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Count at 0 is not 10000", count_added_in_this_bucket == 10000); + } + mu_assert("Count should not be 0", iter.count != 0); + mu_assert("Count at value iterated to should be count added in this step", + iter.count == count_added_in_this_bucket); + total_added_count += count_added_in_this_bucket; + index++; + } + mu_assert("Total counts should be 20000", total_added_count == 20000); + + return 0; +} + +static char* test_linear_values() +{ + struct hdr_iter iter; + int index; + int64_t total_added_count; + + load_histograms(); + + /* Raw Histogram */ + hdr_iter_linear_init(&iter, raw_histogram, 100000); + index = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.linear.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Count at 0 is not 10000", count_added_in_this_bucket == 10000); + } + else if (index == 999) + { + mu_assert("Count at 999 is not 1", count_added_in_this_bucket == 1); + } + else + { + mu_assert("Count should be 0", count_added_in_this_bucket == 0); + } + + index++; + } + mu_assert("Should of met 1000 values", compare_int64(index, 1000)); + + /* Corrected Histogram */ + + hdr_iter_linear_init(&iter, cor_histogram, 10000); + index = 0; + total_added_count = 0; + while (hdr_iter_next(&iter)) + { + int64_t count_added_in_this_bucket = iter.specifics.linear.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Count at 0 is not 10001", count_added_in_this_bucket == 10001); + } + + total_added_count += count_added_in_this_bucket; + index++; + } + mu_assert("Should of met 10001 values", index == 10000); + mu_assert("Should of met 20000 counts", total_added_count == 20000); + + return 0; +} + +static char* test_logarithmic_values() +{ + struct hdr_iter iter; + int index; + uint64_t total_added_count; + + load_histograms(); + + hdr_iter_log_init(&iter, raw_histogram, 10000, 2.0); + index = 0; + + while(hdr_iter_next(&iter)) + { + uint64_t count_added_in_this_bucket = iter.specifics.log.count_added_in_this_iteration_step; + if (index == 0) + { + mu_assert("Raw Logarithmic 10 msec bucket # 0 added a count of 10000", 10000 == count_added_in_this_bucket); + } + else if (index == 14) + { + mu_assert("Raw Logarithmic 10 msec bucket # 14 added a count of 1", 1 == count_added_in_this_bucket); + } + else + { + mu_assert("Raw Logarithmic 10 msec bucket added a count of 0", 0 == count_added_in_this_bucket); + } + + index++; + } + + mu_assert("Should of seen 14 values", index - 1 == 14); + + hdr_iter_log_init(&iter, cor_histogram, 10000, 2.0); + index = 0; + total_added_count = 0; + while (hdr_iter_next(&iter)) + { + uint64_t count_added_in_this_bucket = iter.specifics.log.count_added_in_this_iteration_step; + + if (index == 0) + { + mu_assert("Corrected Logarithmic 10 msec bucket # 0 added a count of 10001", 10001 == count_added_in_this_bucket); + } + total_added_count += count_added_in_this_bucket; + index++; + } + + mu_assert("Should of seen 14 values", index - 1 == 14); + mu_assert("Should of seen count of 20000", total_added_count == 20000); + + return 0; +} + +static char* test_reset() +{ + load_histograms(); + + mu_assert("Value at 99% == 0.0", hdr_value_at_percentile(raw_histogram, 99.0) != 0); + mu_assert("Value at 99% == 0.0", hdr_value_at_percentile(cor_histogram, 99.0) != 0); + + hdr_reset(raw_histogram); + hdr_reset(cor_histogram); + + mu_assert("Total raw count != 0", raw_histogram->total_count == 0); + mu_assert("Total corrected count != 0", cor_histogram->total_count == 0); + + mu_assert("Value at 99% not 0.0", hdr_value_at_percentile(raw_histogram, 99.0) == 0); + mu_assert("Value at 99% not 0.0", hdr_value_at_percentile(cor_histogram, 99.0) == 0); + + return 0; +} + +static char* test_scaling_equivalence() +{ + int64_t expected_99th, scaled_99th; + load_histograms(); + + mu_assert( + "Averages should be equivalent", + compare_values( + hdr_mean(cor_histogram) * 512, + hdr_mean(scaled_cor_histogram), + 0.000001)); + + mu_assert( + "Total count should be equivalent", + compare_int64( + cor_histogram->total_count, + scaled_cor_histogram->total_count)); + + expected_99th = hdr_value_at_percentile(cor_histogram, 99.0) * 512; + scaled_99th = hdr_value_at_percentile(scaled_cor_histogram, 99.0); + mu_assert( + "99%'iles should be equivalent", + compare_int64( + hdr_lowest_equivalent_value(cor_histogram, expected_99th), + hdr_lowest_equivalent_value(scaled_cor_histogram, scaled_99th))); + + return 0; +} + +static char* test_out_of_range_values() +{ + struct hdr_histogram *h; + hdr_init(1, 1000, 4, &h); + mu_assert("Should successfully record value", hdr_record_value(h, 32767)); + mu_assert("Should not record value", !hdr_record_value(h, 32768)); + + return 0; +} + +static char* test_linear_iter_buckets_correctly() +{ + int step_count = 0; + int64_t total_count = 0; + struct hdr_histogram *h; + struct hdr_iter iter; + + hdr_init(1, 255, 2, &h); + + hdr_record_value(h, 193); + hdr_record_value(h, 255); + hdr_record_value(h, 0); + hdr_record_value(h, 1); + hdr_record_value(h, 64); + hdr_record_value(h, 128); + + hdr_iter_linear_init(&iter, h, 64); + + while (hdr_iter_next(&iter)) + { + total_count += iter.specifics.linear.count_added_in_this_iteration_step; + /* start - changes to reproduce issue */ + if (step_count == 0) + { + hdr_record_value(h, 2); + } + /* end - changes to reproduce issue */ + step_count++; + } + + mu_assert("Number of steps", compare_int64(4, step_count)); + mu_assert("Total count", compare_int64(6, total_count)); + + return 0; +} + +static char* test_interval_recording() +{ + int value_count, i, value; + char* result; + struct hdr_histogram* expected_histogram; + struct hdr_histogram* expected_corrected_histogram; + struct hdr_interval_recorder recorder; + struct hdr_interval_recorder recorder_corrected; + struct hdr_histogram* recorder_histogram; + struct hdr_histogram* recorder_corrected_histogram; + + value_count = 1000000; + hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3); + hdr_interval_recorder_init_all(&recorder_corrected, 1, INT64_C(24) * 60 * 60 * 1000000, 3); + hdr_init(1, INT64_C(24) * 60 * 60 * 1000000, 3, &expected_histogram); + hdr_init(1, INT64_C(24) * 60 * 60 * 1000000, 3, &expected_corrected_histogram); + + for (i = 0; i < value_count; i++) + { + value = rand() % 20000; + hdr_record_value(expected_histogram, value); + hdr_record_corrected_value(expected_corrected_histogram, value, 1000); + hdr_interval_recorder_record_value(&recorder, value); + hdr_interval_recorder_record_corrected_value(&recorder_corrected, value, 1000); + } + + recorder_histogram = hdr_interval_recorder_sample(&recorder); + + result = compare_histograms(expected_histogram, recorder_histogram); + if (result) + { + return result; + } + + recorder_corrected_histogram = hdr_interval_recorder_sample(&recorder_corrected); + result = compare_histograms(expected_corrected_histogram, recorder_corrected_histogram); + if (result) + { + return result; + } + + return 0; +} + +static char* reset_histogram_on_sample_and_recycle() +{ + struct hdr_interval_recorder recorder; + struct hdr_histogram* inactive1; + struct hdr_histogram* sample1; + struct hdr_histogram* sample2; + + inactive1 = NULL; + + hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3); + + hdr_interval_recorder_record_value(&recorder, 1234); + + sample1 = hdr_interval_recorder_sample_and_recycle(&recorder, inactive1); + + mu_assert("Should have at least one value", compare_int64(1, sample1->total_count)); + + sample2 = hdr_interval_recorder_sample_and_recycle(&recorder, sample1); + sample1 = hdr_interval_recorder_sample_and_recycle(&recorder, sample2); + + mu_assert("Should have been reset", compare_int64(0, sample1->total_count)); + + return 0; +} + +static struct mu_result all_tests() +{ + mu_run_test(test_create); + mu_run_test(test_invalid_init); + mu_run_test(test_create_with_large_values); + mu_run_test(test_invalid_significant_figures); + mu_run_test(test_total_count); + mu_run_test(test_get_min_value); + mu_run_test(test_get_max_value); + mu_run_test(test_percentiles); + mu_run_test(test_recorded_values); + mu_run_test(test_linear_values); + mu_run_test(test_logarithmic_values); + mu_run_test(test_reset); + mu_run_test(test_scaling_equivalence); + mu_run_test(test_out_of_range_values); + mu_run_test(test_linear_iter_buckets_correctly); + mu_run_test(test_interval_recording); + mu_run_test(reset_histogram_on_sample_and_recycle); + + mu_ok; +} + +static int hdr_histogram_run_tests() +{ + struct mu_result result = all_tests(); + + if (result.message != 0) + { + printf("hdr_histogram_test.%s(): %s\n", result.test, result.message); + } + else + { + printf("ALL TESTS PASSED\n"); + } + + printf("Tests run: %d\n", tests_run); + + return result.message == NULL ? 0 : -1; +} + +int main() +{ + return hdr_histogram_run_tests(); +} diff --git a/test/hdr_test_util.h b/test/hdr_test_util.h new file mode 100644 index 0000000..caa1bc2 --- /dev/null +++ b/test/hdr_test_util.h @@ -0,0 +1,32 @@ +/** + * hdr_test_util.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_HISTOGRAM_HDR_TEST_UTIL_H +#define HDR_HISTOGRAM_HDR_TEST_UTIL_H + +static char* compare_histograms(struct hdr_histogram* expected, struct hdr_histogram* actual) +{ + struct hdr_iter expected_iter; + struct hdr_iter actual_iter; + + hdr_iter_init(&expected_iter, expected); + hdr_iter_init(&actual_iter, actual); + + while (hdr_iter_next(&expected_iter)) + { + mu_assert("Should have next", hdr_iter_next(&actual_iter)); + mu_assert("counts mismatch", compare_int64(expected_iter.count, actual_iter.count)); + } + + mu_assert("Min mismatch", compare_int64(expected->min_value, actual->min_value)); + mu_assert("Max mismatch", compare_int64(expected->max_value, actual->max_value)); + mu_assert("Total mismatch", compare_int64(expected->total_count, actual->total_count)); + + return 0; +} + + +#endif diff --git a/test/hiccup.140623.1028.10646.hlog b/test/hiccup.140623.1028.10646.hlog new file mode 100644 index 0000000..eb6309a --- /dev/null +++ b/test/hiccup.140623.1028.10646.hlog @@ -0,0 +1,16 @@ +#[Logged with jHiccup version 2.0.3-SNAPSHOT] +#[Histogram log format version 1.01] +#[StartTime: 1403476110.183 (seconds since epoch), Mon Jun 23 10:28:30 NZST 2014] +"StartTimestamp","EndTimestamp","Interval_Max","Interval_Compressed_Histogram" +0.042,5.004,0.115,HISTiQAAAEV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQt4QmrkKQvNugkq8g9KKUJoHSjMzoAJGhlEwCkbBKBgFo2AUDDkAANEpBrU= +5.046,5.000,1.294,HISTiQAAAEh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQm5QBhuEYraF0LyuEJoxFypvA6UFoTQzwygYBZQDxtEgGAWjYBSMgoEBAItJBaw= +10.046,5.000,3.113,HISTiQAAAEZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQlFQBiMqzX8RQjNJQMVDobQwlGZCo0fB8ASMo0EwGs+jYDS+R8HwBAC+mAXU +15.046,5.005,0.131,HISTiQAAAEd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQhlQBiOU5oBQAuxQ4a9QcW8oLQKlmRlQASPDKBgFo2AUjIJRMAqGDAAAoysF8A== +20.051,4.995,0.147,HISTiQAAAEp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQglQBlSegRlCCXhAaMbtUHE3KM0PpVmhNCMDKkDnj4JRMApGwSgYBaNgEAIAJgsF4A== +25.046,5.000,0.115,HISTiQAAAEZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQilQBiOUZoNQAnJQ4bNQ8UgoLQylWaA01NxRMApGwSgYBaNgFAwlAABjqwXo +30.046,5.000,0.131,HISTiQAAAEJ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQqkMqIARQgl4Q7n7oOJeUJoPSjNh1zcKRsEoGAWjYBSMgqEAAHWdBeo= +35.046,5.000,0.115,HISTiQAAAEV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQqlQBlSegRlCCWhDaMYzUPFgKC0CpRnR6FEwCkbBKBgFo2AUDCEAAHRlBeo= +40.046,5.000,0.131,HISTiQAAAER42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQikMqIAZQgloQWjGy1DxACjNi6oOTjMyjIJRMApGwSgYBaNgyAAAZDsF6A== +45.046,5.000,0.131,HISTiQAAAEl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQvFQBlSegQNC8W2BCp+Bis+H0hpQmgtKM0NpRoZRMApGwSgYBaNgFAwZAADuSAbd +50.046,5.000,0.246,HISTiQAAAEt42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMQvFQBlSegQ1CMd6G0PxuUPFbUNoJSotDaUY0mlRArr5RMApGwSgYBaNgFFAAAOooBt0= + diff --git a/test/jHiccup-2.0.1.logV0.hlog b/test/jHiccup-2.0.1.logV0.hlog new file mode 100644 index 0000000..e38e3ec --- /dev/null +++ b/test/jHiccup-2.0.1.logV0.hlog @@ -0,0 +1,85 @@ +#[Logged with jHiccup version 2.0.1] +#[Histogram log format version 1.01] +#[StartTime: 1438869961.225 (seconds since epoch), Thu Aug 06 07:06:01 PDT 2015] +"StartTimestamp","EndTimestamp","Interval_Max","Interval_Compressed_Histogram" +0.116,1.005,3.031,HISTiQAAAG542pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+gDFYozQmlOaA0M5Rmg9LsUJoFjc+GZg4rmjkwdUxofG4obQeldaG0LJTmR1PvCKXVIRQjzL2BUFoYTT0jwyjABkbDZTSeR8FofI+CEQ4AUBEGxA== +1.121,1.000,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA+hDEY0mhVKc0BpdijNAqW50NSx4dDPhqafDYf5OlDaGEpLQmleNPs8oLQy1LoYVD5cPcydUH+PglEwCkbBKBgFowAZAABAZwbC +2.121,0.999,0.459,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcYsANmNJoNB82Jpo4Fjc8NpVlhFkJpLijNDqV1obQGlFZEMx+mLghKK0AoxigoXxVKi0NpRjT/MDKMglEwCkbBKBgFowAOAAw5Brw= +3.120,1.002,0.442,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPehDEYozYxGs0BpVjQ+F5o+dhz6OWAWoZkDU88GpXWhtCqU1kDTD9MXAqXVodb7QvlmUJoXzZ0w942CUTAKRsEoGAWjAAkAABrTBr4= +4.122,1.000,0.475,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcYsAOoegY2ND6MZoHSXFCaHU09jM+Kpp4NB60DpfWhtByU5kYzLxBK60EoRncoXwNKS0BpZhz+YmQYBaNgFIyCUTAKRgEDAArJBrw= +5.122,1.000,0.442,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZUAEjTAJKs0BpNjRxGJ8dSnOgibPioFlw8JWhtBqUloXSPFCaC0qHQWltqHPDoXwtKC2MZj4jwygYBaNgFIyCUTAKMAAA+GAGug== +6.122,1.001,0.459,HISTiQAAAGd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZsANGmAIozQKlWaE0M5RmQxOHqeNAU8eOpp4Lja8JpbWhtC6U5kQzPwRKa0CdaQPlm0JpYTRzGdHoUTAKRsEoGAWjYBQAAQD4sAa6 +7.123,0.997,0.541,HISTiQAAAGt42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+gDBYozYhGs6HJw/isaOpY0dTBaA4ozQmlmdHkeaG0HZR2gdKKUJodzV5VKO0Kpe9D6UooLQaludDcRywgVf0oGAWjYBSMglEwJAEAhtAHww== +8.120,1.001,0.459,HISTiQAAAGJ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZsANGKM0MUwilWXHQMHXsaHwWNBomzonGl4PSulBaEUoLoZkrD6XdofQbKL0CSsuiqUf3zygYBaNgFIyCUTAKgAAAJX4HuQ== +9.121,0.999,0.442,HISTiQAAAGJ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+hDBY0mhWNhomzodEweSYc5jDjMIcTjTaC0i5QWhVK86Cpk4DSDlD6NZSuh9KSUJoDSjMyjIJRMApGwSgYBaMAAwAAlsoHxQ== +10.120,1.001,0.459,HISTiQAAAGB42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8ZUAETGs0KpZmhNDsazYKmDpd+GM2NQ582lFZDoznR7FeE0iEQilEKyi+E0vxo+mCAkWEUjIJRMApGwSgYBXAAADJXBsI= +11.121,1.000,0.442,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPegDEYozQyTgNLsaOLMaOpZ0NSzQmkuHPIw8zjRzFOB0jpQWhFK80NpHigtDaVdoPR3KD0dSkug2Q9z5ygYBaNgFIyCUTAKkAAAOxAHuw== +12.121,2.238,1568.670,HISTiQAAAOl42u2aMQ7CMAxFnZaqILEhISQYOAIjJ2Bk40YM7FyAMzBxPCRwhkYqlUpTTPL+8uXEdv6312zO14uIFPLCcStSHnaP2zuU6VyacAF7VMraRyZBXAfnZcBVy71/Z6m8Vl55gUG+17tXviuflBct+sAwcIyAPQP2DNgz/vGNX3wb1OUy3QPzxreF/r+qz+1ddDOvGHWu572L1DdVXcyTPVvS5TrqrMTozEMn8xtHpzUujOtDJ3NMSV/Rk8sv6/+tH77pl6JO/18m/KfTxtbzUvPDfPAdM8//s5t1xGPloWeY+loA+IAnHpcMHQ== +14.359,0.761,0.459,HISTiQAAAGp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTFZQBhuUZsHBZ4bSHFCaFY2Gms/AjqafFYe5MPP4oLQWlLaB0mJQWhhKc0FpfihtBKX7oHQilBZAswfmLkaGUTAKRsEoGAWjYBTAAQCDmgZz +15.120,1.000,0.442,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcZUAEjGs2GRnOi8VmgNDManxtNnBlNH9Q9DOxQWhBK60FpeTRxDijNC6XDofRpKH0JSguj2c/IMApGwSgYBaNgFIwCDAAAR8IHvQ== +16.120,1.002,0.475,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPehDEY0mhmNZoHS7Gg0CxoNU8+GQx4mzoTGl4PSulBaA0qLQGluKM0Ppb2g9EYofRdKi6Kph9nLgOa/UTAKRsEoGAWjYEQDAEgCB70= +17.122,1.001,0.475,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTE8ZUAEjlGaFKUCj2aA0N5RmgdLsaPIsOPgcaHyYuTZQ2hBKq0BpUSjNCaWFobQvlD4IpVdCaXEozYXmH3R6FIyCUTAKRsEoGNEAAK+GB8k= +18.123,1.001,0.426,HISTiQAAAGJ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8ZsAOoegYWHDQblGZFo7mhNCeU5sKhnx2NlofSZlBaEUqLQmkOKC0EpcOg9H4ovQVKy6G5ZxSMglEwCkbBKBgFWAAAjYoHxQ== +19.124,0.996,0.426,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTE9gDCjNBqVZ0GhmKM2KQx2MzwmleaA0OxrNhGYeTL0hlHaC0gY4zOOH0ilQeh2U7oHSMlCai2EUjIJRMApGwSgYBTgBAKssB8c= +20.120,1.003,0.459,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8ZUAFUHQMjlGZBo1mhNDsOPgeaek4ozQyluaA0N5p+ESitC6W10NTB3CUMpUOh9EYofQFKy6PZw4zmn1EwCkbBKBgFo2AUAAEAiFIHxQ== +21.123,0.997,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHdhDCjNiMaH0cxQmgNNnAuNzwalWdHMY0ETR1cnAaUdoLQylBZGswfG94bSZ6H0DigtCqV50OwfBaNgFIyCUTAKRgESAAArzge5 +22.120,1.000,0.475,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTO+gDBYozQylWaE0B5o8D5RmQ6PZoTQnmn6YODcanw3NvEQo7QGlZaA0L5r9YlDaHEp3Q+kVaPLo+mCAkWEUjIJRMApGwSgYBQwAU9cH2w== +23.120,1.002,0.442,HISTiQAAAGd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8ZUAFUHQMLGs0OpdmgNCuaOIzPCaWZ0fSzotFsaPaJQWkdKC0BpXnQaFEoHQulL0DpTVBaBkoLobljFIyCUTAKRsEoGAVIAABnpgfB +24.122,1.001,0.475,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTM9gDDSahQEVsKGJc0JpdhzizFCaFUrzQGkONH1cUNoISjtDaTUoLYCmng9Ku0Dp+VD6KJQWgtL8DNgBI8MoGAWjYBSMglEwChgAxMAHyw== +25.123,0.997,0.459,HISTiQAAAGt42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8ZsANmKM0CpbmgNCeUZofSrGh8DijNhmYejM+NZh7MfBkobQyllaE0H5q9wlDaD0qfh9KroLQ4mvlQfzMwMoyCUTAKRsEoGAWjAA4AcjYHwQ== +26.120,1.003,0.426,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTE8ZUAEjlGZFo5mhNBuU5oTSLGg0O5o+FjQ+OxoNk1eF0gZQ2gRK86GpF4LSPlB6I5Q+BaVFoTQvwygYBaNgFIyCUTAKcAIArGYHyQ== +27.123,0.997,0.475,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcZUAEzGs0KpVmgNBuaOBcanxNNHQuaPIxmh9KMUFoMSutBaWUoLYLmHgkoHQ2l70DpJWjqOdHMR6dHwSgYBaNgFIyCEQ0ATtIHvQ== +28.120,1.004,0.623,HISTiQAAAG142pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8ZUAEzlGaDKYDSrGjyXFCaHU2eA43PiKaPBc1cmD2SUNoUSqtDaX40+0SgdCCU3g+lT0JpYSjNg2Y/Aw4+uYBa5oyCUTAKRsEoGAUDAgCJQgfF +29.124,0.997,0.459,HISTiQAAAGd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTM+gDGYozYJGc+CQZ4XS3FCaHUpzouljQ1PPhibPB6UNobQmlDZBM48LSotBaTcovQFKb4fSPGg0DDAyjIJRMApGwSgYBaMADgDKMAfL +30.121,0.999,0.475,HISTiQAAAG542pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8YUAELlGZG48NoqDkMvFCaE0qz46A5oDQblOZCMx+mTgpKG0FpTSgtAKUFobQYlHaC0jCPHIfSslCaD0ozormbkWEUjIJRMApGwSgYBQwAgpgHww== +31.120,1.000,0.475,HISTiQAAAGp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8YUAEzlGaEKUATZ4PS7FCaBU0dB5RmRaNZ0PSxoYnLQmlrKK0GpfnQzJeG0o5Qei+UvgClJaE0J5q7GdD8NQpGwSgYBaNgFIxoAABYbAe/ +32.120,1.004,0.459,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8ZUAEjlGaB0qxoNNQcBnY0deji6Hx0c2D6mKG0JJRWh9JKUFoQSnNCaSEonQil10HpJ1BaHIc7GBlGwSgYBaNgFIyCUQAHAGMmB8E= +33.124,0.996,0.492,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA+gDEY0mgVKM0NpNhw0K5Rmh9KcUJoDTR07DhomrwSlbaC0Mpo5MPVCUNoPSh+H0mugtCiU5oF5kAE7YGQYBaNgFIyCUTAKRjAAAGPcB78= +34.120,1.003,0.442,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTG+gDGYozYJGw8Q5oDQnlGZDk0cXZ0cT50QzlxVKc0NpHSjtAqWNobQgmj5xKG0NpTdD6RVQWhhK88E8yDAKRsEoGAWjYBSMAgwAACgzB9c= +35.123,0.998,0.475,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPdhDCjNiEazQGkOKM2KRjOj0Rxo5rGjqUc3ByYvC6X1obQulBaG0mxQWhRKu0LpLVD6MpQWgdJcaO5iQPPXKBgFo2AUjIJRMKIBAEwqB70= +36.121,1.003,0.475,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTE8ZUAELGg3Vx8CBJs4NpdmhNCuUZkZTx4qmDibOiWauHJQ2htLqUFoYSnNBaSEoHQylD0DpVWjmwNzHiIMeBaNgFIyCUTAKRjQAAK6eB8k= +37.124,0.998,0.442,HISTiQAAAGp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8YUAEHlGaB0sxQmg2NZofSrGg0Gxofpo4LzVwONFoBShtCaTUozQulOaG0EJQOgNI7oPRuKC0CpflgHmQYBaNgFIyCUTAKRgEGAACDQAfD +38.122,1.001,0.524,HISTiQAAAGt42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+hDEYozQKl2dDE2dBomDpWKM2MQ54dSnOgyTOh8SWhtD6UVoPSvGjmSEDpFCj9GUo3Q2lhKM2D5i6YP3ABQvKjYBSMglEwCkbBsAIAi7IHxQ== +39.123,1.001,0.475,HISTiQAAAG542pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTO8YUAELlGaD0pxQmhtK80JpDjR1glCaC0ozo5nHgWYeE5q4LpQOhdLKUFoYSvNAaQko7QylJ0LpZWjqYe5kRPMfOn8UjIJRMApGwSgYkQAAVCcH2w== +40.124,1.000,0.442,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTC+hDEY0mhWN5ofSXFCaDUpzQGl2KM2MQ5wDh7kwcxSgtBmUNobSslBaCEpLQukgKL0FSvdCaREozQmlWRhGwSgYBaNgFIyCUYABAPuGB9E= +41.124,0.996,0.475,HISTiQAAAGd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+hDGaYAJRmgdJsUJoDjWZB08eFJs+OJg8TZ8WhThNKG0FpPSjNj0ZLQGlvKL0ISm+D0mJQmhPNP4xo9CgYBaNgFIyCUTCiAQCYogfF +42.120,1.004,0.475,HISTiQAAAGp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTC9hDCjNCqXZ0PgsOMRhfHYozYEmzwyledDkWdDUKUBpWyhtCqXFoDQXlBaC0m5QeiGU3gmlRdHUwfwFA4wMo2AUjIJRMApGwShgAADzjgfR +43.124,0.996,0.442,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcYUAFUHQMzlGZHE2dCE4fRLDhoDjQ+Nw5xGSitDaVVobQwlGaF0uJQOgVKL4bSu6C0CpQWgNKMDKNgFIyCUTAKRsEowAAAPDAHuw== +44.120,1.004,0.459,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8YUAEjDj4rTAManwVKs6PxOdDUM6OpY0WjRaC0NZRWhtICUJobTV0wlL4IpTdAaSUozYVmL7q/RsEoGAWjYBSMghENAFMcB78= +45.124,0.999,0.459,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTDcZsANGmAI0mhlNnhVKs0FpFjT1rDhoFjS+GJTWgdKKUJofzXwYPwJK74fSl6G0KpTmQbOHkWEUjIJRMApGwSgYBXAAAN73B7E= +46.123,1.001,0.442,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMzGxQBlSegQNKs0JpdijNB6W5oDQbGs2Bph5Gs8AsQqNZUNUxhkH5hVBaFs0cJjT75KG0C5S2Q3MHzHxGhlEwCkbBKBgFo2AUYAAAlrEGDQ== +47.124,1.409,1233.125,HISTiQAAAMt42u2asQ3CMBREzzZG0DEABSMwBSU1FdNQ0LMAYzAeEpiCL1kCR8YmeddcfvL/9925zfp0OUvyemC/kcJue7s+S8VjepgldnqHN6xMX8jsCWbezsXEh8SrTJ+tQV2QN/cMuGfAPZMHfvGN33/Q5/DNufhudo5r7A/d6Cav4fOucE+r7+giT3QP19VrjU7yQ2c9nb2w71QXOslxzPr8lxwK5369d2r78M2+En79txNNneOp9eGbvjH7WSZeGP70fa2+uQAA1XAH+BcJvg== +48.533,0.591,0.311,HISTiQAAAFR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMjNegDKg8AxeUZkGjWdFoTijNjqaODY2GqWdEU8eMqo5RHMpPRnMHTN8oGAWjYBSMglEwCqgIAEz/Bas= +49.124,1.000,0.442,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTH+hDHYozQGlWaA0M5RmhdJsaDQ7Gp8Djc+Iwzw09YzcUH41lJZBs5cXzXxlKO0DpdOgNDeau6D+HgWjYBSMglEwCkYBMgAA774G+g== +50.124,1.000,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTLcZUAETGp8ZSjNCaRYozYomz46mH5c+dhzmy0JpbSitBqX5oTQHlOaD0n5QeibUGph75ND0oftnFIyCUTAKRsEoGAVAAADL1Aa2 +51.124,1.000,0.442,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZsANWKM2CxofR7FCaEYc6qH0MbGg0TJwTzRwY3xJKi0FpPijNgcaPhdIbofQjKK0EpbnQ3DcKRsEoGAWjYBSMAiQAACOuB7k= +52.124,1.000,0.475,HISTiQAAAGd42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8YUAFUHQMrlGZD46PTHFCaB0ozo+lDp5nQ1PFDaVEobYDGh5nLAqUFoXQalJ4DpZ9DaXEozYlmDyMaPQpGwSgYBaNgFIxoAAB6gAfD +53.124,0.999,0.442,HISTiQAAAF542pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTO+gDHY0mhNK8+AQZ0UTZ8GhjhOH+TD9vFDaEUp7QGllKM2BZr4Mmrq5UHohmnkwfcwMo2AUjIJRMApGwSjAAABYjwfb +54.123,1.001,0.442,HISTiQAAAGt42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTJ+gDGYozQuluaE0B5RmgdJcUJodSvOgibOg0cxo6tnRxIWgtCuUjoTS0lCaD0pzoqk3h9IdUHorlBZHczcjwygYBaNgFIyCUTAKMAAAl8cH4w== +55.124,0.999,0.442,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI+gDEYozQylWaA0K5o41BwGNjRxbjQ+Kw5z0M3jhdJqUNocSkui0TB1IlDaD0pvhdLHobQglOZC89coGAWjYBSMglEwCpAAAH2wB8M= +56.123,1.001,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTB9hDCjNCaU5oDQXDj4blObFoY4VBw2TZ4fSQlDaF0qbQGllKM2P5i5JKO0KpSdA6eVo5sHUMzKMglEwCkbBKBgFowADAACIDQfh +57.124,0.999,0.459,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTA8YUAEblGaE0swwhVCaA0qzQGl2KM2Kpg5dPTsazYKmThFKm0JpFSjNi2aOIJQOhdLLofQlKC2Npo4RjR4Fo2AUjIJRMApGARAAAFlsB78= +58.123,1.000,0.442,HISTiQAAAGB42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZsAMmHDQLlGaE0hxQmg1Ks6PxWXHoZ0FTJwWlTaC0OpTmQzMHZl8glF4OpX+jqRdAc+coGAWjYBSMglEwCpAAACNmB7k= +59.123,1.001,0.442,HISTiQAAAF942pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTE9gDCjNgkZzoMmzQml2NHVsaHxmKM2Ipp4Zhz5dKO2KxudDs18QSjtD6alQ+gGUFsOhbxSMglEwCkbBKBgFSAAAnxwHxw== +60.124,0.999,0.492,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTLcZUAEjGs0GpVnQ+MxoNEyeFUqzo5nDiaafDU2dBJRWhdLSUJoDTT8flPaB0rOh9A8orQSlZXD4Cxd/FIyCUTAKRsEoGFEAAAFqB7U= +61.123,0.999,0.442,HISTiQAAAGB42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTDcYUAFUHQMjGh+XODMazQKl2QioY0VTLwqlDaG0MJTmRzNPEEqHQeldUPoLlJaF0lxo9o2CUTAKRsEoGAWjAAkAAMvVB68= +62.122,1.000,18.481,HISTiQAAAIp42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTI8ZsAMmHDQrlOaC0mxQmhlKc6Cpg9EsaOpg4pxQWgpKW0FpSSgthKZOFEo7Q2mYR95CaQEozYPDP4wMo2Akg9H4H43nUTAaz6NgNF5G/Tvq71H/Di5/jto76u9Rewev/YxDPBxGwSgYBUgAAP6WB8U= +63.122,1.001,0.459,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTM8ZUAErTAJKs0Fpdhx8VjQ+TB8LGp8Vhz6YeQlQ2hxKS0BpHijNBaX5obQ7lF4GpbdBaRk0cxnR6FEwCkbBKBgFo2AUAAEA1joHzQ== +64.123,1.000,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPehDEaYAAMqYEGTZ4fSHGjqOdDUw2hWND4zGs0GpWWhtBEaXwBK80FpISgdA6VXQ+mLaPp40OwdBaNgFIyCUTAKRgESAABF0ge9 +65.123,0.999,0.442,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcYUAEjlGaB0swE+Gxo4mxo8qxofA40cRgtC6UtoLQ8lOaF0lxQmhtKu0PpxVD6C5SWhNL8aP4ZBaNgFIyCUTAKRgESAAA10Ae7 +66.122,1.001,0.459,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTM+hDFYozYxGw8TZoDQHmjg3mjyMZoTSLGjqYXx2NPNUoLQrlNaA0nxQmhNKC0Fpfyi9BErvgdLiaPoY0ehRMApGwSgYBaNgFAABANPaB80= +67.123,0.999,0.442,HISTiQAAAGF42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPdhDDSaHUqzoPFZoTQnlGaD0hw4zGFG04euHsaXg9KaUJoPSvOgqReF0hFQej2UfoBmDrp7RsEoGAWjYBSMglGABABLoge9 +68.122,1.001,0.475,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHcZsANGNBqqn4ENSrNCaWY0cRYCfG4ozQGl2aG0IJTWgdJiaPKcUJoPSsdC6R1Q+g2UlkWzDx0wMYyCUTAKRsEoGAWjgAEAIHYHuQ== +69.123,0.999,0.459,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTLcYiAOMUJoNSjOj8dnQ1LNCaXYozYmmjwVKc0BpAShtC6WFoDQ3mnoYPwFK74LSd6C0LJQWhHkQzf2jYBSMglEwCkbBKAACAO+JB7M= +70.122,1.001,0.442,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTDcZsANGKM0CpZnRaFY0PhManw1NnAnNPFY0cUUobQSleaA0N5p7OKG0PZTuhkrbQvnSUFoEzfxRMApGwSgYBaNgFCABAKc4BrI= +71.123,0.999,0.426,HISTiQAAAFh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTHehDBaYAAGaBQfNjEazofFZcfBhtDqUtofSEmjmwOwXgdKBULofQjGiy8PoUTAKRsEoGAWjYBRgAQDzCAa6 +72.122,1.001,0.442,HISTiQAAAF142pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTNcZsANGHHwWKM2MRrPgoJnQaBYc+vigtAaUFoDSgmjqeKB0IpReBHWeDJQPo3lx+GMUjIJRMApGwSgYBUAAAIOkBq4= +73.123,1.001,0.475,HISTiQAAAGB42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTNcYsAOoegZGND4LGp8BTR0zGs2Kph6XuSJQ2hBKC0FpPjRzeKC0D5RuhhpnD+XLoulnweHOUTAKRsEoGAWjYEQDAHGaBqw= +74.124,0.999,0.442,HISTiQAAAGJ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTLcYsANGKM0MU4hGs0BpVjQ+G5o6mH52NHlWNHl5KG0JpYWgNBeaOhg/HErPgtJ/obQMmn4OhlEwCkbBKBgFo2AUYAAA7wEHsw== +75.123,1.000,0.459,HISTiQAAAGh42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTLehDGaYAJRmZEAFTGjq0NXDaBY0GqaOC0rzoIlzQGk1KK0DpQWgNBuUZkfTHwylm6HOlYPypdH0M6K5bxSMglEwCkbBKBgFQAAAzUwGtg== +76.123,1.000,0.475,HISTiQAAAGR42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTDcZUAFUHQMjGp8FBw1Tx4YmzoYmz4zDPJi8GJQ2htLCUFoQzTwRKO0BpeugxuhC+bJQWgjNPgY0+0bBKBgFo2AUjIIRDQCpiAay +77.123,1.001,0.459,HISTiQAAAGZ42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPcZsAOoegZmNBpdnBVKc0FpFjR5DjR9LGg0TFwbSptCaRkoLYBmviCUDobSG6D0BSitAKW50cxnZBgFo2AUjIJRMApGARwAAET6B70= +78.124,0.999,0.475,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTFcZsANGNBqqn4EFSrOh8WHqWNHUw2hmHPIwfZxQWgdK80FpDjSaF0p7Q+lpUGPsoXxRKC2Mw32MDKNgFIyCUTAKRsEoYAAAYlAGqg== +79.123,0.999,0.459,HISTiQAAAGV42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTPdgDCjNAqUZ0WiYPDOUZkMTZ8VhDisOdTB5TiitB6U9obQIlOZC0ycApV2gdC/UmTB3SqPpY0bzxygYBaNgFIyCUTAKgAAABCkGvA== +80.122,1.001,0.459,HISTiQAAAGN42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTNcY8ANGKM2Mxoeax8ACpVlxqGNDE0enYeaIQ2lVKC2Aph9Gc0DpWCg9HWqdDpQvCaVF0cxnZBgFo2AUjIJRMApGARwAAHIqBqw= +81.123,0.999,0.557,HISTiQAAAGl42pNpmdzBwMDAxAAGfgoMDMxuBjsWQLgMTFcZiANQ/QxsUJoRSrNAaWY0PjsaH109K5q5glBaHUrzQ2leNPUcUNobSndDjXeG8sWhtDCafQxo7iAEiFU3CkbBKBgFo2AUDEkAAGNwBqo= diff --git a/test/jHiccup-2.0.6.logV1.hlog b/test/jHiccup-2.0.6.logV1.hlog new file mode 100644 index 0000000..3356576 --- /dev/null +++ b/test/jHiccup-2.0.6.logV1.hlog @@ -0,0 +1,92 @@ +#[Logged with jHiccup version 2.0.6] +#[Histogram log format version 1.1] +#[StartTime: 1438867590.285 (seconds since epoch), Thu Aug 06 06:26:30 PDT 2015] +"StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram" +0.133,1.005,2.802,HISTIgAAAFd42pNpmazIwMAYxgABTBDKT4GBgdnNYMcCBvsPUBkeBkYGZqA8MwMbAzsDC5DFBCTZgJCDQY1BjkGLQZRBlUEPCB8zWDCYMxgDZZkZhgJgHDibAY8JB/A= +1.138,0.998,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACMQsQJKRgYOBlYEZCFmAkB3IB/HkGJQYJBi4gXIyDPGMTAxzGKQZ2EC6AJ7YBtg= +2.136,1.001,0.475,HISTIgAAAEt42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACIfMYMjCwArEzEA9TEBSgkGJQZCBn4GLQYDBh+ESw2cGYSBkYWAEAKZvB9Q= +3.137,1.001,0.492,HISTIgAAAE542pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPUBlGsCwTkGYFYg4GFgZmIIsFDNmBcrIMGgz8DDxAMTGGNIZHDPsZpIFskHlMALndB9o= +4.138,0.999,0.492,HISTIgAAAE142pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPUBkWBjY4BpGsQMwOFGcG6mEBQl8GOwZRBj6gGBtDBMMOhpUMUgxcQDkmBkYAwSAH4w== +5.137,1.003,0.459,HISTIgAAAEx42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPcBmQHBuYZmZgZWABYnYgZmNgBLI5GXQYrBj4wSKiDB4MexgeMwgw8DKwAgCbcgfb +6.140,0.998,0.492,HISTIgAAAE542pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDMiAGaiClYGRgR3MYmdgA5JsQDYLgxKDBgM/kGYBkq4MFxg+MEgyCAFVAs0EALiCB9c= +7.138,1.001,0.475,HISTIgAAAE942pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACMQsDMxAyAaE7ECaFcxnAdLMDMoMagxCDBxAcTGGIIZbDBcZRBm4geYxAQCqKAfZ +8.139,0.997,0.459,HISTIgAAAFB42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDACMTMQBXsDCxANguQzcLABoQsQMjKIMegzsADFGNmkGBIYGRmWMIgw8DLwAQAj9EG1Q== +9.136,1.004,0.475,HISTIgAAAEx42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDMiACYyZGRiBEESzACEzkMXCoMmgzCDAwM/AysDH4MXwhOE5gwiDIFA1IwCmuAfX +10.140,0.996,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBk2IGYBQiYGZgZWIGQBk0wM7AyMQDYbgy6DOQMHAzeQJcTgzHAFCMWBfGYAm8UH2A== +11.136,1.233,1035.993,HISTIgAAAJx42pNpmazIwMD5jgECmCCUnwIDA7ObwY4FDPYfGFABRAUjGDNCWcxgzMnAChZjYeBn0GbQY2ADsoYCYBy1eRCYyjiA/mGkqnpGiuxiHBI2MxLQy0g12cFiMyNJNP1V0d5MRhTMiCGCC1Nb5cDYjB0y4ZShvUr6msgEhsxQGhUyU1lsIHXjMpEFClmxsLCLks4aHOawMDACAO56ClU= +12.369,0.771,0.459,HISTIgAAAEx42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPSDJMDCxAyMTADMSMQMwGJFmBIiC2NIM8gxADO1BMjMGdYT5DL4M4Ax8DEwCR7Acv +13.140,0.996,0.459,HISTIgAAAEp42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjACJRnYmBmYAHTrEAWI5hkAdKSDKoMXEDIxiDI4MlwnJGDQQLIYgIAjsYG0A== +14.136,1.001,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjAyMACxMxAVSxAyAqk2YAYJMLAIMmgwSDIwAkUFWXwYzjL8JNBnEGAgREAlsUH1A== +15.137,1.002,0.459,HISTIgAAAEx42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPcBlGBmYGViCEkExAmomBBYp5GYwYdBkkGfgY2BiEGFwZLjJcYBBm4GJgBACcBQfc +16.139,0.998,0.475,HISTIgAAAEt42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDMiAEQiZGViBkAUIWcE62IEsZgYpBm0GHiCbk0GQwYfhAcMjBjEgC6geAKdjB9Q= +17.137,1.003,0.475,HISTIgAAAEl42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDMiACQxZGRgZ2ME0M5BkBrNUGLQZBICizAzcDL4M1xneMEgz8AFlGAGnqwfY +18.140,0.998,0.442,HISTIgAAAE142pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPDAxIcswMjAwsYMjAwAaErEBRFqAYI4MDgzKDKAMfgziDHIM5w0WGkwxiDLwAiiQH1Q== +19.138,1.000,0.459,HISTIgAAAE542pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjABITMDIwMXECanYGVgYWBDcxiBpKKDCoMQgzcQBEBBh+GjwynGCQZOBiYAJl6B9c= +20.138,1.002,0.557,HISTIgAAAFF42pNpmazIwMDgwgABTBDKT4GBgdnNYMcCBvsPUBlWBhagLAiyACEzWC0jkMUBxIwMbAyaDJYMfAzsQB4/gxvDM4ZTDNIMPEAZBGAEAP1VB+Y= +21.140,0.998,0.459,HISTIgAAAEt42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDACIRAYTBkY2AFqmVjYAFiBiCbmUGQQROIuYGiggwejLwMuxhkgCxGAI58BtQ= +22.138,1.000,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACJRlZGBlYIaqYmdgA0IWoBgjUEyGQZlBCMhnYhBlCGOUYFjLIMnACdIBAJ0WBtg= +23.138,0.998,0.492,HISTIgAAAEx42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDADJYFkSxADKLZgJgJyldmUGEQYOBhYGXgYwhlZGTYxyDMwM/ACNTBCACrbwbX +24.136,1.003,0.573,HISTIgAAAE942pNpmazIwMDgxgABTBDKT4GBgdnNYMcCBvsPDDDACITMQBXMDBxAkgUIQWxmMIuFQYFBkYGPgQ0oJsUQwsjMsJ1BkkEAZh7MBAD0rQbn +25.139,1.000,0.459,HISTIgAAAEx42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDACCbZGFiBkAOokpGBBchiY2AGQxkGTQZBoAgbAy+DKyM7wykgT4CBEQCPpwbX +26.139,0.997,0.475,HISTIgAAAEx42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPUBlGIGQFyjMDSVYGFjBkgoqyALEigzqDMAMnAy+DGIMfoyTDNAYhBh6QCgCfhQbZ +27.136,1.004,0.442,HISTIgAAAEp42pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPDDDAyMAMxixQdSxANgMDGxAyA6EigyoDJwM3AzsDH4M3w3eGGwyyDDwAhpMH1A== +28.140,0.996,0.459,HISTIgAAAEt42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBkWoBwIMgNZ7ECaHcxiYWAEk1wMBgxmDGIM3EC+IIMLw2eGTQz8QFEmAJxuB9k= +29.136,1.000,0.492,HISTIgAAAEp42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDKiAEQhZGNiAmBkIGRlYwXwWBmEGZQZ+MEuQIYWRi2EbgwoDB1AOqAMAqa0G1w== +30.136,1.003,0.459,HISTIgAAAE542pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDACIQgwMzAxsAChKxAmhlIMwJZzAxSDEoMvAzsQCjE4MPwnuE+gwQDPwMjAJclB9Y= +31.139,0.998,0.459,HISTIgAAAFF42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBkuBmagLBMDCwMbkGRkYAVCFiBmAGJmBj4GRQZdBlmgKjYg6cDwk2E+gwQDDwMzAJzYB9s= +32.137,0.999,0.459,HISTIgAAAEp42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPSDKMYMzCwM7AysAMFGEC0iAWCwM3gwyDBgM/mCfIEMfIzrAeSPMzMAIAj9AG1g== +33.136,1.004,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDACJbnAGJWMGRiYAZCJgYWIGRmkGNQZRAAyjIzcDKEMrIy7GGQYRBkYAQAj3YG2g== +34.140,0.999,0.442,HISTIgAAAEl42pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPDDDACITMQMwGh6wMLEDIBBRjZZBiUGLgYeAAsngZ3BleMTxmkGHgBQCIIAfT +35.139,1.001,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPcBkWIGZiYAZCCJuZgY2BFYhZgCQXgwmDNgM/AzuQJ8bgzHCX4TyDCAM3AyMAm0EH2g== +36.140,0.996,0.459,HISTIgAAAEp42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDMiAkYEFjNmAKpmhkBUImRjEGOQZ+BnYGTgYJBhCGD4zXGMQZeBhYAQAlpQH0Q== +37.136,1.004,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPUBlGIGRgYAWqYGZgA5IsQJoFzAPxmRkUGYwZhIAsVgYZBmeG3wwHGCQYOEC6AKnrB9w= +38.140,0.996,0.492,HISTIgAAAE542pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDAjAyMAMhEwM7EDMCoZsQDEOoBgLgzyDIgMPkM/KIMJgxfCI4SODOAMv2DxGALigB9c= +39.136,1.000,0.459,HISTIgAAAEt42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjAysAIVMECpFmANBsDB5AG8ZiAWI5BhUEKLCbBEMzIyTCPQYyBl4EJAJEUBtg= +40.136,1.000,0.492,HISTIgAAAE942pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPUBkWIGYGQkYgZgGqY2JgB9LMYMgGhIYMDgyyDBwMrAz8DCEMzxlmMkgDWSDACAC9Gwfg +41.136,1.002,0.492,HISTIgAAAE542pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPcBkWIMkMJFmBkJmBF4hBLBYGTqAcO4Migw+DAAMHUFSMwYjhHMNNII8frJMRAL4nB+I= +42.138,0.998,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAjAyMAMxKxAyMTAAmSzAVnMQMgC5EszaDFwMXAAefwMngyvGZ4wiDIIgPQAAKfOB9U= +43.136,1.002,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAxIciwMzAxsQAgimYA8RiDJAeSxMygwKDEIMHAC2YIMvgwvGC4zSDHwA+UZAalrB9o= +44.138,1.002,0.492,HISTIgAAAEx42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDAjACMTMQMgKhCCaBcxiYmADyigyKDFwM7AzcDGIMzgzCjBsBdICQFmgLgCrjQbb +45.140,0.996,0.459,HISTIgAAAEt42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBkWBkYwZGFgBaoCYTYgyQ7EbEAxNgZjBiMGfjBfjMGB4QPDLgYhII8JAJv1B9g= +46.136,1.003,0.492,HISTIgAAAFF42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPcBlGIGZhYGVgBtLsDBxAmh3MYwHKsDMYMOgySDBwAfmiQNOOMDxiEGIQBsowMDACALywB+E= +47.139,1.001,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDMiAkYGZgQWImYCYmYEViBmBJBNQTJxBg0GEgZOBjYGPIYjhGsNroIgwUIYRAKbXB9Y= +48.140,1.000,0.442,HISTIgAAAEt42pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPDAjAxMDIwAzG7AysDFxAkpmBBUwyMtgwGDIIM/AycDCIMpgz3GI4CaR5AItmB9k= +49.140,1.000,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDMiAiYERCNkZmIGQlYENyGdmYAGKszCIMcgwcAMhFwM/QzjDK4anQBF+BkYAli4H0w== +50.140,2.452,1895.825,HISTIgAAAKx42pNpmazIwMB1hAECmCCUnwIDA7ObwY4FDPYfGGCAEYiZwWoYgZgZzGcCQwYGVgZhBhEGbgZ2BjYgW4+hjqGcgR/IYgSrohxQx5RRm+lpM+OA6aaXzYwDJDtQNjPiVUmKLC1V08pmRqJoRhqoHhw2E8aMA6aS9jYThkxEqSJe3eAykQkJMqPwSBMbSN2UmMjKwAKHrFRhDS1zOBk4gJATijmw8ge5GgDvng6P +52.592,0.546,0.442,HISTIgAAAEl42pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPUBlmoBwLEDMBWcxgkhEqxgpksTNIMQgxcDGwAfl8DDIM1Qy5DLwMrAB+SwaO +53.138,1.002,0.475,HISTIgAAAE142pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPcBlGBmYgZGRgAWImIMkMFmEDslgYOBnMGEwYJBjYGbgZeBkSGG4xrAXzgGoAq7YH3Q== +54.140,1.000,0.524,HISTIgAAAE942pNpmazIwMDgwAABTBDKT4GBgdnNYMcCBvsPDAjACIQgNWxAzMLACsTMQMwEZDEzKDLIMwgycANZ4gz+DI8Y7jBIM/AA5RghOgHYtgfd +55.140,0.996,0.475,HISTIgAAAEh42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDMiAGaiCBQwZwZgZCNnAumQY1Bh4gDwWBkEGZ4bLjMwMkgxCQBlGAJ0MBtI= +56.136,1.000,0.492,HISTIgAAAE942pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPUBkWoBwjEINIZgZWMIuVgQOIWRnYgLKaDPYMXGBRDgYvhhsMVxhkGASB+hgZGAG66gfd +57.136,1.003,0.492,HISTIgAAAFF42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDACIQMDCxAkomBFUizMLAxMANFmIF8NgZFBnUGYQYuoCgfgzfDR4brQB4/2DwmALhmB9s= +58.139,0.997,0.475,HISTIgAAAEt42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPUBlGqDwjAwsQsgFpNgZmKASJyDMoMAgx8AJV8DA4M9xheMsgziAClGMEAKc6B9M= +59.136,1.003,0.492,HISTIgAAAE542pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDAjACITMUMjIwAJUzczACsYsDHIMqgz8DNwM7AyCDN4MVxneM8gz8ABVAXUBALeqB9s= +60.139,0.999,0.459,HISTIgAAAE542pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBlmoBwLAyOQBrFAkBmMGYFyrAxcDGYMOgwiDAJANbwMrgznGB4ziDJwMjACAJn0B9c= +61.138,1.001,0.492,HISTIgAAAE942pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDACJRlYmBmYGVgY2AB8piBkAuIWYCiLAySDM4MQmAZMQZ/hg8MuxhEGHgg+gC7NQfe +62.139,0.999,0.475,HISTIgAAAEt42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAgAkmUHklwMrAwsQMwMZINYTEAozaDEIAxkMzPIMKQy8jDMA/J4geKMAJ7UBto= +63.138,1.001,13.959,HISTIgAAAGJ42pNpmazIwMC8igECmCCUnwJQzM1gxwIG+w8MDHA5ZiBmZeBgYASS7AwsYD6IzQakVRjUGEQZuICyYgw2DDcY7gLZwkAVIMDIMNQA46jNA2YW45CwmZEikxhpJwsAR+UIUA== +64.139,0.997,0.492,HISTIgAAAE942pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDMiAhYGNgRUIWRiYGdiBmAUI2cGitgx6DJJAmp2BhyGU4SrDbgYBBg6gCkYGRgC9Qwfe +65.136,1.002,0.459,HISTIgAAAE142pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPcBkWBmYGNgZWMGQB8kFsJgZ2BkYg5mXQYNBnEGbgArL5GNwYXjAcANJ8DIwAnS0H3g== +66.138,0.998,0.459,HISTIgAAAEt42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjACIasDOxAkgWoloWBGQiZwGwFBjUGHgYOoBw3gx/DBYbfDMIMQgzMAJalB9I= +67.136,1.002,0.492,HISTIgAAAFF42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDAApRlYeBgYAOymYEkKxAyglnsQBjLYMEgwMDNwAkk7RiOMexi4GfgAsozMDACAMCHB+Y= +68.138,1.002,0.459,HISTIgAAAEp42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDDDAzMAKVgGiWYAQRHIAITOYJ89gySDMwAMUFWJwYtjD8JJBioGPgRkAmrYH2w== +69.140,1.212,763.363,HISTIgAAAKR42pNpmazIwMCZwwABTBDKT4GBgdnNYMcCBvsPDMiAEQiZgJgFiFnBqpmg+vgY+IEizGBxFYZshjQGbgZ2II8WgDamjtpMG5sZB8xu2tvMSJE87WRpYzYjSXxqqh4cNjOi0IxYRXHR9FdFfTMZoSQhTJyqgVVJqnnEQSYqq6OFSvJMZEKBzGh8XGLEq6SPieTrZgG2a1igED+LeJXYWYwAsfYJeg== +70.352,0.784,0.475,HISTIgAAAFJ42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPUBkuBm4GTgYWBl4gZmFgY+AAqmUEYhYGdiDNw2DGYAMk+YGQl0GLoZ6hDUizAfUxAgCtOgdN +71.136,1.001,0.492,HISTIgAAAEt42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDAjACMaMDKxAzMzADibZgCQLUESFQQ1I8gBFuBhCGLYzCjMIMYiAVTMBAKvBBtk= +72.137,1.003,0.475,HISTIgAAAEt42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAjAyMAMxEApBhagSiYgyQjEELYsgzoDLwM7kMfFEMBwlpGLQYaBB6QHAJ0DBtc= +73.140,0.996,0.475,HISTIgAAAE942pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACMTMQJKJgR1IsgLZIB4zAwsQszKIMWgyiANpVgZehgiGMwz3GeQZRIEyTACm5QfS +74.136,1.004,0.459,HISTIgAAAEx42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDAjACJZnZmBlYAFCJiBmBLKZgSQLgyiDJgM3AzsQ8jEkMjxkeAYU4WdgAgCWiAfX +75.140,0.997,0.492,HISTIgAAAEx42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDACJZnBkIWMIsNiFmBoiARJgZJBmUGYSCfnYGXoYCRiWEngwiQDdLDCACq1wbX +76.137,1.000,0.442,HISTIgAAAE142pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPDDDACJZnYmBmYANiJiCfmYEFCFmBLE4GeQYFBn4gzcIgxuDNcIzhB4M0gwgAho8H0g== +77.137,1.003,0.459,HISTIgAAAE942pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPDMiAkYGZgRWoioWBE8hiZmADirABeawMUgxaDHwMHECeAIMfw2WGrwwiDFwMzACYHQfY +78.140,1.000,0.459,HISTIgAAAFB42pNpmazIwMBgwQABTBDKT4GBgdnNYMcCBvsPUBlmIGYEyjMDISsDCwMbkAbx2IBsRgYOBj8GDwZJBnYgX4TBlmEdw2kGUQZ+BkYAnacH3g== +79.140,0.998,0.475,HISTIgAAAE142pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAjACJRnApLsDMxgkoGBBchiA2JmBhEGHQZBoBgbAz+DL8Npht8M4gwCINUApvkH1A== +80.138,1.002,0.508,HISTIgAAAE542pNpmazIwMBgxwABTBDKT4GBgdnNYMcCBvsPDDDACIQsDMxAzMTACqSZgBgE2RjYgaQUgxaDIFhMnCGY4Q7DXQYJBn4gD6wTAMlCB90= +81.140,0.997,0.557,HISTIgAAAFB42pNpmazIwMDgwgABTBDKT4GBgdnNYMcCBvsPUBlWoBwjAzOQZAFCZiCbhYENyGOFimkz2DPwM/AB+dwMTgzHGJ4zCDDwgNXBACMA/WwH4w== +82.137,0.999,0.475,HISTIgAAAE542pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDDDACMZMQMjCwArEzAxsDJxgEWYgKc+gzsDHwA0UF2AIYrjK8IJBnIEfKMcIAKfXB9Y= +83.136,1.003,0.475,HISTIgAAAEx42pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPDAxwOUYgycrADIQscMwGFAexpBgUGbgZuBg4GAQZYhluMTxhEGYQAupgBACn3gfZ +84.139,1.001,0.492,HISTIgAAAEx42pNpmazIwMBgwwABTBDKT4GBgdnNYMcCBvsPDDDACIWsDMwMbGCVzGAeA5DHzCDBoMsgzMAB5MszeDI8ZLjCIMfAC1bBCAC31AfZ +85.140,0.996,0.442,HISTIgAAAEt42pNpmazIwMBgxgABTBDKT4GBgdnNYMcCBvsPUBl2IGQByrMCSTYGDiBkBfNYwSK8DG0M7gzCDJxAOX4GC4ZFDLMZRBg4AZVqB+U= +86.136,1.001,0.557,HISTIgAAAFF42pNpmazIwMDgwgABTBDKT4GBgdnNYMcCBvsPDAjACISsDCxAVSCSDUiD2MxAUTYGcQZNBiEgn5VBgiGU4Q7DYwYxBm6gDAMYg3UDAPppB+I= +87.137,0.999,0.541,HISTIgAAAE142pNpmazIwMDgxAABTBDKT4GBgdnNYMcCBvsPDAjACITMQDUsQJIVTLIAaRDJwiDMoAnEnAwcDCIMVgxXGP4ySDHwwMwD6QUA6PAH3Q== +88.136,1.002,0.475,HISTIgAAAE142pNpmazIwMBgxQABTBDKT4GBgdnNYMcCBvsPUBlGIGQCYxYGVgZ2IMkGxIwMzEAaREowaDLwM3AAebYM6ozdDE4MIkBVQF0An+EG3w== diff --git a/test/jHiccup-2.0.7S.logV2.hlog b/test/jHiccup-2.0.7S.logV2.hlog new file mode 100644 index 0000000..f9e801f --- /dev/null +++ b/test/jHiccup-2.0.7S.logV2.hlog @@ -0,0 +1,66 @@ +#[Logged with jHiccup version 2.0.7-SNAPSHOT] +#[Histogram log format version 1.2] +#[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015] +"StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram" +0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI= +1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM= +2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo= +3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo= +4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk= +5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw== +6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw== +7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE= +8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw +9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M= +10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR +11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ== +12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko +13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA= +14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+ +15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw== +16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne +17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0 +18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg== +19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ== +20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw== +21.135,0.999,0.492,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhk1AjINDRECAj4+Hi49LKS5CS2EGo1kXa4ANExMDEwAmOQil +22.134,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBFmHhE+MRExCTEZAS4RMQERvRI1hSuMTidY3KQ4mAAXhgks +23.131,1.004,0.508,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhotHSEBASEyMg09MQUSIT6tKS2YKY8gfFj8tJmYmJgAsowkz +24.135,0.998,0.492,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEBLjkhETEpET4BISEhCR6FsqAQFY1jjBoTWPQEOJiZAC2aCUY= +25.133,1.002,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBkuHh4BITEpMSEpLiE5AS6FoAgdpQuMJk9YzMRYmAAdngk2 +26.135,0.998,0.508,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUAkOKSEJKTUJOT4+IQkeIT69LYwVCnIbGI0eMZtJsTAxMwEAQvkJyg== +27.133,0.998,0.442,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBE2CQUZFTkZOSURKQkRMT6NKYwhbYxaOocY/a4xSUmwAQA4pQpb +28.131,1.002,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGtFDcHIy0jDQUdPjENFZUzjNNYHCT4uBQkzJiYADIGCcY= +29.133,1.460,968.884,HISTFAAAAJZ42pNpmSzMwMDUwgABTBDKT4GBgdnNYMcCBvsPEBE5AwMDJSUFISk2ETYuAS6PQ0xSXCzsTEw7GZnKgdCTyZLJGog1maSZZIFYGkpLMnEz8QIhOolgcTKxAiEzmGRFYxMShbEYUCAalzRBsjSjARYmTIBNjDKFSIIsIMDGAgPYWJRJE1DIxQEEaAQHF2GCNDVsAE2dFJE= +30.593,0.541,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEFCxUNBRkFMTE+Pj4ZHgGHFYwGIkJcMiIpbEwMTAAdQQhJ +31.134,0.997,0.737,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEJGAHsYexqKaIAcPPRMVKTEhoR6mJUxqfBx8LFwCTOxM0kwAfR8KqA== +32.131,1.002,0.508,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEJKCDMcHJw8jOTUfNSEZGQuUb4x9GHxkJDg2hMA4WViYmAHWrC2k= +33.133,1.000,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGXGK8QHS09PRM9BRMxBa55jBOY03REhByE3DhYADicCkc= +34.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBE1NzsfJwMVEw0pFS0hOZm4FqYKPy2FAoUJjFIsTAA/mQql +35.131,1.000,0.459,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBERMy0jPTk5LRUFJQk1GamYdUzHGO0UxIrUljBKsbEwAQBKXgqU +36.131,1.001,0.557,HISTFAAAAEd42pNpmSzMwMCgygABTBDKT4GBgdnNYMcCBvsPEBExJzcNMyU5PRUpLSkJKYWwHqYWRjslkTKNC4wKHGwMTExArUwAi/IKnA== +37.132,1.002,0.442,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEFLRsVPQkTKTkhPT4ZBTm3V4yTGD20pFoYtZqYxESYAEjICok= +38.134,1.000,0.803,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNERM7Hwk3LRslMSkZMQExDLGQL0yTGIC2pKJ1VjCwcTJpMAFufCso= +39.134,0.997,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE5Oz8DPRsFORM5FQkNKaGCA8wtjCoSfBYSTYxCLEBtTABiWgor +40.131,1.000,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBExJQUNFTElFRUZBRUZDTGfJqYKHzmhHka5ZUwSQmwANK0J+g== +41.131,1.002,0.475,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE2Hj45PiEFGSU5EQkpKREJuVmMLYwaWk8YQyYwa3CxMTABAEOgCdQ= +42.133,1.000,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBk+Lg4+ER4hMT4hIT4lLh69OAOZZ4wOr1hCpFiYABjUCSY= +43.133,1.002,0.442,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBFmLgEJMTERHjEuCRERBSERoww5rRuMendYPFRYAA3tCTM= +44.135,0.998,0.590,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBk+FT0lJTktJSUjOTE1OQGpmnOMdnorGF3WMemxCTIBAEAhCnU= +45.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWMS0DIyMFOSsNPTEFMSGNA4x+LxidfOp0VjBKcLAAAECLCv4= +46.131,1.004,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEuMS0VEyMlLSkzGQUJOSkJj6RnjE56WxjNWpik2JgAO34KfQ== +47.135,0.996,0.475,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUgk2GR0ZOQkSAR4aLS0KKTyNtDqOWxjVGu2fMGlJMTEwANsIJvA== +48.131,1.950,1803.551,HISTFAAAAKF42pNpmSzMwMD0mQECmCCUnwIDA7ObwY4FDPYfICKsTExMLCysLCxsbEwMTAIsDHIsWTwsbNsZmcqZKpncmayZLIFYnUmWSRoMIbQkEy8TNxQjkwgWJxMrGDJDaews/KIMKBCNSytBZCYqYGHCBNjEiBckoJAFBNhYYADBwipIhkIC0lwcQIBGcHARJqigBkwKCQgICSAIFA75IlwAeB8ZpQ== +50.081,0.050,0.393,HISTFAAAADl42pNpmSzMwMAgxgABTBDKT4GBgdnNYMcCBvsPEBE2BiYWNiYWZiYGJiZmJg4OLiYuFiYWAMWGBSM= +50.131,1.001,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2Lj4VAQkuJT45KTkOKSExI68eRgeDvB2MfcxxckwAJD8JyA== +51.132,0.999,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2NgUFGSkNAQEeJSkuKSmxhAojhZADjKuYiyS4WAAlWgm/ +52.131,1.002,0.557,HISTFAAAAER42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPUBkWPjEFGSMZKQMJJSEhPgkJiyodjZIHjB+YSvh4mBiYWJkAVc8KVw== +53.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2HjklJR0VPSUDHTUxJSkJs02MuxhtrLxKHjH6cbEAADjeCuw= +54.131,1.003,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkNPzMLIw0NLQ0pFTERCTGLT4wpQSVbGFcwynExAQA/uwsC +55.134,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPUBkWFTUjCy01BQ0VFRUJGSkJjRamiqA5jHmXGIV4ACoyCmo= +56.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUhk1FzsrAQElFQ0xCQkJOTEDnE6ObxwrGDsYuJjUODiYASN8KbA== +57.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPMBk5FT0JAzUNKTklKQ0FMaGUJ4wJFjcYk+4wqnAwMAEAQooK6Q== +58.131,1.002,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEuCRMNJwMlIzUtLR0ZMREZv6IHjFYGdUXLGE14WAA4OwsG +59.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBklExUdIwcdFRUlOTMZPhWXB4wBTssYsy4xKnGwAQA8bAry +60.131,1.000,0.524,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmIRcjPR0bFR0lDSk5KQkZpXlMXkF5qxh3MMqIcDIBADy8CoE= +61.131,1.000,26.083,HISTFAAAAF542pNpmSzMwMAQyAABTBDKT4GBgdnNYMcCBvsPMBkFHSMrCzEZLSUFCSkJOTmTf4xRQW2MYT8Y5diYdjIylTNVMrkzWTJZA7EmkzQYykJpSSZeJm4ghpAQFgATDg85 diff --git a/test/jHiccup-2.0.7S.logV3.hlog b/test/jHiccup-2.0.7S.logV3.hlog new file mode 100644 index 0000000..5e3e0c8 --- /dev/null +++ b/test/jHiccup-2.0.7S.logV3.hlog @@ -0,0 +1,66 @@ +#[Logged with jHiccup version 2.0.7-SNAPSHOT] +#[Histogram log format version 1.3] +#[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015] +"StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram" +0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI= +Tag=a1.134a,1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM= +2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo= +Tag=a3.134a,3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo= +4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk= +Tag=a5.132a,5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw== +6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw== +Tag=a7.133a,7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE= +8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw +9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M= +10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR +11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ== +12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko +13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA= +14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+ +15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw== +16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne +17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0 +18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg== +19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ== +20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw== +21.135,0.999,0.492,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhk1AjINDRECAj4+Hi49LKS5CS2EGo1kXa4ANExMDEwAmOQil +22.134,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBFmHhE+MRExCTEZAS4RMQERvRI1hSuMTidY3KQ4mAAXhgks +23.131,1.004,0.508,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhotHSEBASEyMg09MQUSIT6tKS2YKY8gfFj8tJmYmJgAsowkz +24.135,0.998,0.492,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEBLjkhETEpET4BISEhCR6FsqAQFY1jjBoTWPQEOJiZAC2aCUY= +25.133,1.002,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBkuHh4BITEpMSEpLiE5AS6FoAgdpQuMJk9YzMRYmAAdngk2 +26.135,0.998,0.508,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUAkOKSEJKTUJOT4+IQkeIT69LYwVCnIbGI0eMZtJsTAxMwEAQvkJyg== +27.133,0.998,0.442,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBE2CQUZFTkZOSURKQkRMT6NKYwhbYxaOocY/a4xSUmwAQA4pQpb +28.131,1.002,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGtFDcHIy0jDQUdPjENFZUzjNNYHCT4uBQkzJiYADIGCcY= +29.133,1.460,968.884,HISTFAAAAJZ42pNpmSzMwMDUwgABTBDKT4GBgdnNYMcCBvsPEBE5AwMDJSUFISk2ETYuAS6PQ0xSXCzsTEw7GZnKgdCTyZLJGog1maSZZIFYGkpLMnEz8QIhOolgcTKxAiEzmGRFYxMShbEYUCAalzRBsjSjARYmTIBNjDKFSIIsIMDGAgPYWJRJE1DIxQEEaAQHF2GCNDVsAE2dFJE= +30.593,0.541,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEFCxUNBRkFMTE+Pj4ZHgGHFYwGIkJcMiIpbEwMTAAdQQhJ +31.134,0.997,0.737,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEJGAHsYexqKaIAcPPRMVKTEhoR6mJUxqfBx8LFwCTOxM0kwAfR8KqA== +32.131,1.002,0.508,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEJKCDMcHJw8jOTUfNSEZGQuUb4x9GHxkJDg2hMA4WViYmAHWrC2k= +33.133,1.000,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGXGK8QHS09PRM9BRMxBa55jBOY03REhByE3DhYADicCkc= +34.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBE1NzsfJwMVEw0pFS0hOZm4FqYKPy2FAoUJjFIsTAA/mQql +35.131,1.000,0.459,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBERMy0jPTk5LRUFJQk1GamYdUzHGO0UxIrUljBKsbEwAQBKXgqU +36.131,1.001,0.557,HISTFAAAAEd42pNpmSzMwMCgygABTBDKT4GBgdnNYMcCBvsPEBExJzcNMyU5PRUpLSkJKYWwHqYWRjslkTKNC4wKHGwMTExArUwAi/IKnA== +37.132,1.002,0.442,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEFLRsVPQkTKTkhPT4ZBTm3V4yTGD20pFoYtZqYxESYAEjICok= +38.134,1.000,0.803,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNERM7Hwk3LRslMSkZMQExDLGQL0yTGIC2pKJ1VjCwcTJpMAFufCso= +39.134,0.997,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE5Oz8DPRsFORM5FQkNKaGCA8wtjCoSfBYSTYxCLEBtTABiWgor +40.131,1.000,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBExJQUNFTElFRUZBRUZDTGfJqYKHzmhHka5ZUwSQmwANK0J+g== +41.131,1.002,0.475,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE2Hj45PiEFGSU5EQkpKREJuVmMLYwaWk8YQyYwa3CxMTABAEOgCdQ= +42.133,1.000,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBk+Lg4+ER4hMT4hIT4lLh69OAOZZ4wOr1hCpFiYABjUCSY= +43.133,1.002,0.442,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBFmLgEJMTERHjEuCRERBSERoww5rRuMendYPFRYAA3tCTM= +44.135,0.998,0.590,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBk+FT0lJTktJSUjOTE1OQGpmnOMdnorGF3WMemxCTIBAEAhCnU= +45.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWMS0DIyMFOSsNPTEFMSGNA4x+LxidfOp0VjBKcLAAAECLCv4= +46.131,1.004,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEuMS0VEyMlLSkzGQUJOSkJj6RnjE56WxjNWpik2JgAO34KfQ== +47.135,0.996,0.475,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUgk2GR0ZOQkSAR4aLS0KKTyNtDqOWxjVGu2fMGlJMTEwANsIJvA== +48.131,1.950,1803.551,HISTFAAAAKF42pNpmSzMwMD0mQECmCCUnwIDA7ObwY4FDPYfICKsTExMLCysLCxsbEwMTAIsDHIsWTwsbNsZmcqZKpncmayZLIFYnUmWSRoMIbQkEy8TNxQjkwgWJxMrGDJDaews/KIMKBCNSytBZCYqYGHCBNjEiBckoJAFBNhYYADBwipIhkIC0lwcQIBGcHARJqigBkwKCQgICSAIFA75IlwAeB8ZpQ== +50.081,0.050,0.393,HISTFAAAADl42pNpmSzMwMAgxgABTBDKT4GBgdnNYMcCBvsPEBE2BiYWNiYWZiYGJiZmJg4OLiYuFiYWAMWGBSM= +50.131,1.001,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2Lj4VAQkuJT45KTkOKSExI68eRgeDvB2MfcxxckwAJD8JyA== +51.132,0.999,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2NgUFGSkNAQEeJSkuKSmxhAojhZADjKuYiyS4WAAlWgm/ +52.131,1.002,0.557,HISTFAAAAER42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPUBkWPjEFGSMZKQMJJSEhPgkJiyodjZIHjB+YSvh4mBiYWJkAVc8KVw== +53.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2HjklJR0VPSUDHTUxJSkJs02MuxhtrLxKHjH6cbEAADjeCuw= +54.131,1.003,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkNPzMLIw0NLQ0pFTERCTGLT4wpQSVbGFcwynExAQA/uwsC +55.134,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPUBkWFTUjCy01BQ0VFRUJGSkJjRamiqA5jHmXGIV4ACoyCmo= +56.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUhk1FzsrAQElFQ0xCQkJOTEDnE6ObxwrGDsYuJjUODiYASN8KbA== +57.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPMBk5FT0JAzUNKTklKQ0FMaGUJ4wJFjcYk+4wqnAwMAEAQooK6Q== +58.131,1.002,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEuCRMNJwMlIzUtLR0ZMREZv6IHjFYGdUXLGE14WAA4OwsG +59.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBklExUdIwcdFRUlOTMZPhWXB4wBTssYsy4xKnGwAQA8bAry +60.131,1.000,0.524,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmIRcjPR0bFR0lDSk5KQkZpXlMXkF5qxh3MMqIcDIBADy8CoE= +61.131,1.000,26.083,HISTFAAAAF542pNpmSzMwMAQyAABTBDKT4GBgdnNYMcCBvsPMBkFHSMrCzEZLSUFCSkJOTmTf4xRQW2MYT8Y5diYdjIylTNVMrkzWTJZA7EmkzQYykJpSSZeJm4ghpAQFgATDg85 diff --git a/test/minunit.c b/test/minunit.c new file mode 100644 index 0000000..e889640 --- /dev/null +++ b/test/minunit.c @@ -0,0 +1,25 @@ +#include + +#include "minunit.h" + +bool compare_double(double a, double b, double delta) +{ + if (fabs(a - b) < delta) + { + return true; + } + + printf("[compare_double] fabs(%f, %f) < %f == false\n", a, b, delta); + return false; +} + +bool compare_int64(int64_t a, int64_t b) +{ + if (a == b) + { + return true; + } + + printf("[compare_int64] %" PRIu64 " == %" PRIu64 " == false\n", a, b); + return false; +} diff --git a/test/minunit.h b/test/minunit.h new file mode 100644 index 0000000..3a22fb5 --- /dev/null +++ b/test/minunit.h @@ -0,0 +1,52 @@ +/** + * minunit.h + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef MINUNIT_H +#define MINUNIT_H + +#include +#include +#include + +struct mu_result +{ + char* test; + char* message; +}; + +#define mu_assert(message, test) \ + do { \ + if (!(test)) \ + return message; \ + } while (0) + +#define mu_run_test(name) \ + do { \ + char *message = name(); \ + tests_run++; \ + if (message) { \ + struct mu_result r; \ + r.test = #name; \ + r.message = message; \ + return r; \ + } \ + } while (0) + +#define mu_ok \ + do { \ + struct mu_result r; \ + r.test = 0; \ + r.message = 0; \ + return r; \ + } while (0) + +extern int tests_run; + +bool compare_double(double a, double b, double delta); + +bool compare_int64(int64_t a, int64_t b); + +#endif