From 3d868e33d481dc3baba8f0b3abea10d69b1d7c00 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 18 Jan 2025 23:26:54 +0100 Subject: [PATCH] wip iidxio-async Summary: Test Plan: --- Module.mk | 7 + src/main/hdr-histogram/Module.mk | 10 + src/main/hdr-histogram/hdr_atomic.h | 146 ++ src/main/hdr-histogram/hdr_encoding.c | 322 ++++ src/main/hdr-histogram/hdr_encoding.h | 79 + src/main/hdr-histogram/hdr_endian.h | 116 ++ src/main/hdr-histogram/hdr_histogram.c | 1226 ++++++++++++++++ src/main/hdr-histogram/hdr_histogram.h | 516 +++++++ src/main/hdr-histogram/hdr_histogram_log.c | 1290 +++++++++++++++++ src/main/hdr-histogram/hdr_histogram_log.h | 236 +++ .../hdr-histogram/hdr_histogram_log_no_op.c | 171 +++ .../hdr-histogram/hdr_histogram_version.h | 12 + .../hdr-histogram/hdr_interval_recorder.c | 227 +++ .../hdr-histogram/hdr_interval_recorder.h | 109 ++ src/main/hdr-histogram/hdr_malloc.h | 19 + src/main/hdr-histogram/hdr_tests.h | 22 + src/main/hdr-histogram/hdr_thread.c | 108 ++ src/main/hdr-histogram/hdr_thread.h | 55 + src/main/hdr-histogram/hdr_time.c | 107 ++ src/main/hdr-histogram/hdr_time.h | 50 + .../hdr-histogram/hdr_writer_reader_phaser.c | 142 ++ .../hdr-histogram/hdr_writer_reader_phaser.h | 51 + src/main/iidxio-async/Module.mk | 9 + src/main/iidxio-async/iidxio-async.def | 18 + src/main/iidxio-async/iidxio.c | 414 ++++++ src/main/iidxio-perf/Module.mk | 10 + src/main/iidxio-perf/iidxio-perf.def | 18 + src/main/iidxio-perf/iidxio.c | 306 ++++ 28 files changed, 5796 insertions(+) create mode 100644 src/main/hdr-histogram/Module.mk create mode 100644 src/main/hdr-histogram/hdr_atomic.h create mode 100644 src/main/hdr-histogram/hdr_encoding.c create mode 100644 src/main/hdr-histogram/hdr_encoding.h create mode 100644 src/main/hdr-histogram/hdr_endian.h create mode 100644 src/main/hdr-histogram/hdr_histogram.c create mode 100644 src/main/hdr-histogram/hdr_histogram.h create mode 100644 src/main/hdr-histogram/hdr_histogram_log.c create mode 100644 src/main/hdr-histogram/hdr_histogram_log.h create mode 100644 src/main/hdr-histogram/hdr_histogram_log_no_op.c create mode 100644 src/main/hdr-histogram/hdr_histogram_version.h create mode 100644 src/main/hdr-histogram/hdr_interval_recorder.c create mode 100644 src/main/hdr-histogram/hdr_interval_recorder.h create mode 100644 src/main/hdr-histogram/hdr_malloc.h create mode 100644 src/main/hdr-histogram/hdr_tests.h create mode 100644 src/main/hdr-histogram/hdr_thread.c create mode 100644 src/main/hdr-histogram/hdr_thread.h create mode 100644 src/main/hdr-histogram/hdr_time.c create mode 100644 src/main/hdr-histogram/hdr_time.h create mode 100644 src/main/hdr-histogram/hdr_writer_reader_phaser.c create mode 100644 src/main/hdr-histogram/hdr_writer_reader_phaser.h create mode 100644 src/main/iidxio-async/Module.mk create mode 100644 src/main/iidxio-async/iidxio-async.def create mode 100644 src/main/iidxio-async/iidxio.c create mode 100644 src/main/iidxio-perf/Module.mk create mode 100644 src/main/iidxio-perf/iidxio-perf.def create mode 100644 src/main/iidxio-perf/iidxio.c diff --git a/Module.mk b/Module.mk index 2a948a8..473e49f 100644 --- a/Module.mk +++ b/Module.mk @@ -135,6 +135,7 @@ include src/main/ezusb2-popn-shim/Module.mk include src/main/ezusb2-tool/Module.mk include src/main/ezusb2/Module.mk include src/main/geninput/Module.mk +include src/main/hdr-histogram/Module.mk include src/main/hook/Module.mk include src/main/hooklib/Module.mk include src/main/iidx-bio2-exit-hook/Module.mk @@ -154,9 +155,11 @@ include src/main/iidxhook6/Module.mk include src/main/iidxhook7/Module.mk include src/main/iidxhook8/Module.mk include src/main/iidxhook9/Module.mk +include src/main/iidxio-async/Module.mk include src/main/iidxio-bio2/Module.mk include src/main/iidxio-ezusb/Module.mk include src/main/iidxio-ezusb2/Module.mk +include src/main/iidxio-perf/Module.mk include src/main/iidxio/Module.mk include src/main/iidxiotest/Module.mk include src/main/inject/Module.mk @@ -456,9 +459,11 @@ $(zipdir)/iidx-27-to-30.zip: \ $(zipdir)/iidx-hwio-x86.zip: \ build/bin/indep-32/aciomgr.dll \ build/bin/indep-32/eamio-icca.dll \ + build/bin/indep-32/iidxio-async.dll \ build/bin/indep-32/iidxio-bio2.dll \ build/bin/indep-32/iidxio-ezusb.dll \ build/bin/indep-32/iidxio-ezusb2.dll \ + build/bin/indep-32/iidxio-perf.dll \ dist/iidx/iidxio-bio2.conf \ build/bin/indep-32/vigem-iidxio.exe \ dist/iidx/vigem-iidxio.conf \ @@ -469,9 +474,11 @@ $(zipdir)/iidx-hwio-x86.zip: \ $(zipdir)/iidx-hwio-x64.zip: \ build/bin/indep-64/aciomgr.dll \ build/bin/indep-64/eamio-icca.dll \ + build/bin/indep-64/iidxio-async.dll \ build/bin/indep-64/iidxio-bio2.dll \ build/bin/indep-64/iidxio-ezusb.dll \ build/bin/indep-64/iidxio-ezusb2.dll \ + build/bin/indep-64/iidxio-perf.dll \ dist/iidx/iidxio-bio2.conf \ build/bin/indep-64/vigem-iidxio.exe \ dist/iidx/vigem-iidxio.conf \ diff --git a/src/main/hdr-histogram/Module.mk b/src/main/hdr-histogram/Module.mk new file mode 100644 index 0000000..bda0bf1 --- /dev/null +++ b/src/main/hdr-histogram/Module.mk @@ -0,0 +1,10 @@ +libs += hdr-histogram + +src_hdr-histogram := \ + hdr_encoding.c \ + hdr_histogram_log_no_op.c \ + hdr_histogram.c \ + hdr_interval_recorder.c \ + hdr_thread.c \ + hdr_time.c \ + hdr_writer_reader_phaser.c \ diff --git a/src/main/hdr-histogram/hdr_atomic.h b/src/main/hdr-histogram/hdr_atomic.h new file mode 100644 index 0000000..11b0cbd --- /dev/null +++ b/src/main/hdr-histogram/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) && !(defined(__clang__) && (defined(_M_ARM) || defined(_M_ARM64))) + +#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/main/hdr-histogram/hdr_encoding.c b/src/main/hdr-histogram/hdr_encoding.c new file mode 100644 index 0000000..3564af4 --- /dev/null +++ b/src/main/hdr-histogram/hdr_encoding.c @@ -0,0 +1,322 @@ +/** + * 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-histogram/hdr_encoding.h" +#include "hdr-histogram/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; + } + } + } + } + } + } + } + } + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4146) /* C4146: unary minus operator applied to unsigned type, result still unsigned */ +#endif + value = (value >> 1) ^ (-(value & 1)); +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + *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 & 3u) != 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/main/hdr-histogram/hdr_encoding.h b/src/main/hdr-histogram/hdr_encoding.h new file mode 100644 index 0000000..95f1db6 --- /dev/null +++ b/src/main/hdr-histogram/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/main/hdr-histogram/hdr_endian.h b/src/main/hdr-histogram/hdr_endian.h new file mode 100644 index 0000000..839fdb1 --- /dev/null +++ b/src/main/hdr-histogram/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/main/hdr-histogram/hdr_histogram.c b/src/main/hdr-histogram/hdr_histogram.c new file mode 100644 index 0000000..c2f7408 --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram.c @@ -0,0 +1,1226 @@ +/** + * 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 "hdr-histogram/hdr_histogram.h" +#include "hdr-histogram/hdr_tests.h" +#include "hdr-histogram/hdr_atomic.h" + +#ifndef HDR_MALLOC_INCLUDE +#define HDR_MALLOC_INCLUDE "hdr_malloc.h" +#endif + +#include HDR_MALLOC_INCLUDE + +/* ###### ####### ## ## ## ## ######## ###### */ +/* ## ## ## ## ## ## ### ## ## ## ## */ +/* ## ## ## ## ## #### ## ## ## */ +/* ## ## ## ## ## ## ## ## ## ###### */ +/* ## ## ## ## ## ## #### ## ## */ +/* ## ## ## ## ## ## ## ### ## ## ## */ +/* ###### ####### ####### ## ## ## ###### */ + +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) && !(defined(__clang__) && (defined(_M_ARM) || defined(_M_ARM64))) +# 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) && !(defined(__clang__) && (defined(_M_ARM) || defined(_M_ARM64))) + 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 size_of_equivalent_value_range_given_bucket_indices( + const struct hdr_histogram *h, + int32_t bucket_index, + int32_t sub_bucket_index) +{ + const 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); +} + +static int64_t lowest_equivalent_value_given_bucket_indices( + const struct hdr_histogram *h, + int32_t bucket_index, + int32_t sub_bucket_index) +{ + 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_discernible_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_discernible_value < 1 || + significant_figures < 1 || 5 < significant_figures || + lowest_discernible_value * 2 > highest_trackable_value) + { + return EINVAL; + } + + cfg->lowest_discernible_value = lowest_discernible_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; + + double unit_magnitude = log((double)lowest_discernible_value) / log(2); + if (INT32_MAX < unit_magnitude) + { + return EINVAL; + } + + cfg->unit_magnitude = (int32_t) unit_magnitude; + 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_discernible_value = cfg->lowest_discernible_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_discernible_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_discernible_value, highest_trackable_value, significant_figures, &cfg); + if (r) + { + return r; + } + + counts = (int64_t*) hdr_calloc((size_t) cfg.counts_len, sizeof(int64_t)); + if (!counts) + { + return ENOMEM; + } + + histogram = (struct hdr_histogram*) hdr_calloc(1, sizeof(struct hdr_histogram)); + if (!histogram) + { + hdr_free(counts); + return ENOMEM; + } + + histogram->counts = counts; + + hdr_init_preallocated(histogram, &cfg); + *result = histogram; + + return 0; +} + +void hdr_close(struct hdr_histogram* h) +{ + if (h) { + hdr_free(h->counts); + hdr_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); +} + +static int64_t get_value_from_idx_up_to_count(const struct hdr_histogram* h, int64_t count_at_percentile) +{ + int64_t count_to_idx = 0; + + count_at_percentile = 0 < count_at_percentile ? count_at_percentile : 1; + for (int32_t idx = 0; idx < h->counts_len; idx++) + { + count_to_idx += h->counts[idx]; + if (count_to_idx >= count_at_percentile) + { + return hdr_value_at_index(h, idx); + } + } + + return 0; +} + + +int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile) +{ + double requested_percentile = percentile < 100.0 ? percentile : 100.0; + int64_t count_at_percentile = + (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5); + int64_t value_from_idx = get_value_from_idx_up_to_count(h, count_at_percentile); + if (percentile == 0.0) + { + return lowest_equivalent_value(h, value_from_idx); + } + return highest_equivalent_value(h, value_from_idx); +} + +int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percentiles, int64_t *values, size_t length) +{ + if (NULL == percentiles || NULL == values) + { + return EINVAL; + } + + struct hdr_iter iter; + const int64_t total_count = h->total_count; + // to avoid allocations we use the values array for intermediate computation + // i.e. to store the expected cumulative count at each percentile + for (size_t i = 0; i < length; i++) + { + const double requested_percentile = percentiles[i] < 100.0 ? percentiles[i] : 100.0; + const int64_t count_at_percentile = + (int64_t) (((requested_percentile / 100) * total_count) + 0.5); + values[i] = count_at_percentile > 1 ? count_at_percentile : 1; + } + + hdr_iter_init(&iter, h); + int64_t total = 0; + size_t at_pos = 0; + while (hdr_iter_next(&iter) && at_pos < length) + { + total += iter.count; + while (at_pos < length && total >= values[at_pos]) + { + values[at_pos] = highest_equivalent_value(h, iter.value); + at_pos++; + } + } + return 0; +} + +double hdr_mean(const struct hdr_histogram* h) +{ + struct hdr_iter iter; + int64_t total = 0, count = 0; + int64_t total_count = h->total_count; + + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter) && count < total_count) + { + if (0 != iter.count) + { + count += iter.count; + total += iter.count * hdr_median_equivalent_value(h, iter.value); + } + } + + return (total * 1.0) / 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; + const int64_t value = hdr_value_at_index(iter->h, iter->counts_index); + const int32_t bucket_index = get_bucket_index(iter->h, value); + const int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, iter->h->unit_magnitude); + const int64_t leq = lowest_equivalent_value_given_bucket_indices(iter->h, bucket_index, sub_bucket_index); + const int64_t size_of_equivalent_value_range = size_of_equivalent_value_range_given_bucket_indices( + iter->h, bucket_index, sub_bucket_index); + iter->lowest_equivalent_value = leq; + iter->value = value; + iter->highest_equivalent_value = leq + size_of_equivalent_value_range - 1; + iter->median_equivalent_value = leq + (size_of_equivalent_value_range >> 1); + + 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: + 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/main/hdr-histogram/hdr_histogram.h b/src/main/hdr-histogram/hdr_histogram.h new file mode 100644 index 0000000..59ffe54 --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram.h @@ -0,0 +1,516 @@ +/** + * 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_discernible_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_discernible_value The smallest possible value that is distinguishable from 0. + * Must be a positive integer that is >= 1. May be internally rounded down to nearest power of 2. + * @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_discernible_value is < 1 or the + * significant_figure value is outside of the allowed range, ENOMEM if malloc + * failed. + */ +int hdr_init( + int64_t lowest_discernible_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 construction 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 occurred 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 expected_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 construction 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 occurred 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 expected_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_discernible_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_discernible_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); + +/** + * Get the values at the given percentiles. + * + * @param h "This" pointer. + * @param percentiles The ordered percentiles array to get the values for. + * @param length Number of elements in the arrays. + * @param values Destination array containing the values at the given percentiles. + * The values array should be allocated by the caller. + * @return 0 on success, ENOMEM if the provided destination array is null. + */ +int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percentiles, int64_t *values, size_t length); + +/** + * 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_discernible_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_discernible_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 manually 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/main/hdr-histogram/hdr_histogram_log.c b/src/main/hdr-histogram/hdr_histogram_log.c new file mode 100644 index 0000000..0ea476c --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram_log.c @@ -0,0 +1,1290 @@ +/** + * 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 "hdr-histogram/hdr_histogram.h" +#include "hdr-histogram/hdr_histogram_log.h" +#include "hdr-histogram/hdr_encoding.h" +#include "hdr-histogram/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" + +#ifndef HDR_MALLOC_INCLUDE +#define HDR_MALLOC_INCLUDE "hdr_malloc.h" +#endif + +#include HDR_MALLOC_INCLUDE + +/* 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 const uint32_t V0_ENCODING_COOKIE = 0x1c849308; +static const uint32_t V0_COMPRESSION_COOKIE = 0x1c849309; + +static const uint32_t V1_ENCODING_COOKIE = 0x1c849301; +static const uint32_t V1_COMPRESSION_COOKIE = 0x1c849302; + +static const uint32_t V2_ENCODING_COOKIE = 0x1c849303; +static const uint32_t V2_COMPRESSION_COOKIE = 0x1c849304; + +static uint32_t get_cookie_base(uint32_t cookie) +{ + return (cookie & ~0xf0U); +} + +static uint32_t word_size_from_cookie(uint32_t cookie) +{ + return (cookie & 0xf0U) >> 4U; +} + +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) +{ + memset(strm, 0, sizeof(z_stream)); +} + +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__))*/ +{ + uint32_t cookie; + int32_t significant_figures; + int64_t lowest_discernible_value; + int64_t highest_trackable_value; + int64_t total_count; + int64_t counts[1]; +} encoding_flyweight_v0_t; + +typedef struct /*__attribute__((__packed__))*/ +{ + uint32_t cookie; + int32_t payload_len; + int32_t normalizing_index_offset; + int32_t significant_figures; + int64_t lowest_discernible_value; + int64_t highest_trackable_value; + uint64_t conversion_ratio_bits; + uint8_t counts[1]; +} encoding_flyweight_v1_t; + +typedef struct /*__attribute__((__packed__))*/ +{ + uint32_t cookie; + int32_t length; + uint8_t data[1]; +} compression_flyweight_t; +#pragma pack(pop) + +#define SIZEOF_ENCODING_FLYWEIGHT_V0 (sizeof(encoding_flyweight_v0_t) - sizeof(int64_t)) +#define SIZEOF_ENCODING_FLYWEIGHT_V1 (sizeof(encoding_flyweight_v1_t) - sizeof(uint8_t)) +#define SIZEOF_COMPRESSION_FLYWEIGHT (sizeof(compression_flyweight_t) - sizeof(uint8_t)) + +int hdr_encode_compressed( + struct hdr_histogram* h, + uint8_t** compressed_histogram, + size_t* compressed_len) +{ + encoding_flyweight_v1_t* encoded = NULL; + compression_flyweight_t* 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_t*) hdr_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 | 0x10U); + encoded->payload_len = htobe32(payload_len); + encoded->normalizing_index_offset = htobe32(h->normalizing_index_offset); + encoded->significant_figures = htobe32(h->significant_figures); + encoded->lowest_discernible_value = htobe64(h->lowest_discernible_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_t*) hdr_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 | 0x10U); + compressed->length = htobe32((int32_t)dest_len); + + *compressed_histogram = (uint8_t*) compressed; + *compressed_len = SIZEOF_COMPRESSION_FLYWEIGHT + dest_len; + + cleanup: + hdr_free(encoded); + if (result == HDR_DEFLATE_FAIL) + { + hdr_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_t* 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_t encoding_flyweight; + z_stream strm; + uint32_t encoding_cookie; + int32_t compressed_len, word_size, significant_figures, counts_array_len; + int64_t lowest_discernible_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_discernible_value = be64toh(encoding_flyweight.lowest_discernible_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + if (hdr_init( + lowest_discernible_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*) hdr_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); + hdr_free(counts_array); + + if (result != 0) + { + hdr_free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + hdr_free(h); + } + + return result; +} + +static int hdr_decode_compressed_v1( + compression_flyweight_t* 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_t encoding_flyweight; + z_stream strm; + uint32_t encoding_cookie; + int32_t compressed_length, word_size, significant_figures, counts_limit, counts_array_len; + int64_t lowest_discernible_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_discernible_value = be64toh(encoding_flyweight.lowest_discernible_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + if (hdr_init( + lowest_discernible_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*) hdr_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); + hdr_free(counts_array); + + if (result != 0) + { + hdr_free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + hdr_free(h); + } + + return result; +} + +static int hdr_decode_compressed_v2( + compression_flyweight_t* 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_t encoding_flyweight; + z_stream strm; + uint32_t encoding_cookie; + int32_t compressed_length, counts_limit, significant_figures; + int64_t lowest_discernible_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_discernible_value = be64toh(encoding_flyweight.lowest_discernible_value); + highest_trackable_value = be64toh(encoding_flyweight.highest_trackable_value); + significant_figures = be32toh(encoding_flyweight.significant_figures); + + rc = hdr_init(lowest_discernible_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*) hdr_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); + hdr_free(counts_array); + + if (result != 0) + { + hdr_free(h); + } + else if (NULL == *histogram) + { + *histogram = h; + } + else + { + hdr_add(*histogram, h); + hdr_free(h); + } + + return result; +} + +int hdr_decode_compressed( + uint8_t* buffer, size_t length, struct hdr_histogram** histogram) +{ + uint32_t compression_cookie; + compression_flyweight_t* compression_flyweight; + + if (length < SIZEOF_COMPRESSION_FLYWEIGHT) + { + return EINVAL; + } + + compression_flyweight = (compression_flyweight_t*) 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*) hdr_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: + hdr_free(compressed_histogram); + hdr_free(encoded_histogram); + + return result; +} + +int hdr_log_write_entry( + struct hdr_log_writer* writer, + FILE* file, + struct hdr_log_entry* entry, + 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; + int has_tag = 0; + const char* tag_prefix; + const char* tag_value; + const char* tag_separator; + + (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*) hdr_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); + } + + has_tag = NULL != entry->tag && 0 < entry->tag_len; + tag_prefix = has_tag ? "Tag=" : ""; + tag_value = has_tag ? entry->tag : ""; + tag_separator = has_tag ? "," : ""; + + if (fprintf( + file, "%s%.*s%s%.3f,%.3f,%" PRIu64 ".0,%s\n", + tag_prefix, (int) entry->tag_len, tag_value, tag_separator, + hdr_timespec_as_double(&entry->start_timestamp), + hdr_timespec_as_double(&entry->interval), + hdr_max(histogram), + encoded_histogram) < 0) + { + result = EIO; + } + + cleanup: + hdr_free(compressed_histogram); + hdr_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; +} + +int hdr_log_read( + struct hdr_log_reader* reader, FILE* file, struct hdr_histogram** histogram, + hdr_timespec* timestamp, hdr_timespec* interval) +{ + int result; + struct hdr_log_entry log_entry; + memset(&log_entry, 0, sizeof(log_entry)); + + result = hdr_log_read_entry(reader, file, &log_entry, histogram); + + if (0 == result) + { + if (NULL != timestamp) + { + memcpy(timestamp, &log_entry.start_timestamp, sizeof(*timestamp)); + } + if (NULL != interval) + { + memcpy(interval, &log_entry.interval, sizeof(*interval)); + } + } + + return result; +} + +static int read_ahead(FILE* f, const char* prefix, size_t prefix_len) +{ + size_t i; + for (i = 0; i < prefix_len; i++) + { + if (prefix[i] != fgetc(f)) + { + return 0; + } + } + + return 1; +} + +static int read_ahead_timestamp(FILE* f, hdr_timespec* timestamp, char expected_terminator) +{ + int c; + int is_seconds = 1; + long sec = 0; + long nsec = 0; + long nsec_multipler = 1000000000; + + while (EOF != (c = fgetc(f))) + { + if (expected_terminator == c) + { + timestamp->tv_sec = sec; + timestamp->tv_nsec = (nsec * nsec_multipler); + return 1; + } + else if ('.' == c) + { + is_seconds = 0; + } + else if ('0' <= c && c <= '9') + { + if (is_seconds) + { + sec = (sec * 10) + (c - '0'); + } + else + { + nsec = (nsec * 10) + (c - '0'); + nsec_multipler /= 10; + } + } + else + { + return 0; + } + } + + return 0; +} + +enum parse_log_state { + INIT, TAG, BEGIN_TIMESTAMP, INTERVAL, MAX, HISTOGRAM, DONE +}; + +int hdr_log_read_entry( + struct hdr_log_reader* reader, FILE* file, struct hdr_log_entry *entry, struct hdr_histogram** histogram) +{ + enum parse_log_state state = INIT; + size_t capacity = 1024; + size_t base64_len = 0; + size_t tag_offset = 0; + char* base64_histogram = hdr_calloc(capacity, sizeof(char)); + size_t compressed_len = 0; + uint8_t* compressed_histogram = NULL; + int result = -EINVAL; + + (void)reader; + + if (NULL == entry) + { + return -EINVAL; + } + + do + { + int c; + + switch (state) + { + case INIT: + c = fgetc(file); + if ('T' == c) + { + if (read_ahead(file, "ag=", 3)) + { + state = TAG; + } + else + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + } + else if ('0' <= c && c <= '9') + { + ungetc(c, file); + state = BEGIN_TIMESTAMP; + } + else if ('\r' == c || '\n' == c) + { + /* Skip over trailing/preceding new lines. */ + } + else if (EOF == c) + { + FAIL_AND_CLEANUP(cleanup, result, EOF); + } + else + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + break; + case TAG: + c = fgetc(file); + if (',' == c) + { + if (NULL != entry->tag && tag_offset < entry->tag_len) + { + entry->tag[tag_offset] = '\0'; + } + state = BEGIN_TIMESTAMP; + } + else if ('\r' == c || '\n' == c || EOF == c) + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + else + { + if (NULL != entry->tag && tag_offset < entry->tag_len) + { + entry->tag[tag_offset] = (char) c; + tag_offset++; + } + } + break; + case BEGIN_TIMESTAMP: + if (read_ahead_timestamp(file, &entry->start_timestamp, ',')) + { + state = INTERVAL; + } + else + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + break; + case INTERVAL: + if (read_ahead_timestamp(file, &entry->interval, ',')) + { + state = MAX; + } + else + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + break; + case MAX: + if (read_ahead_timestamp(file, &entry->max, ',')) + { + state = HISTOGRAM; + } + else + { + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + break; + case HISTOGRAM: + c = fgetc(file); + if (c != '\r' && c != '\n' && c != EOF) + { + if (base64_len == capacity) + { + capacity *= 2; + base64_histogram = hdr_realloc(base64_histogram, capacity * sizeof(char)); + if (NULL == base64_histogram) + { + FAIL_AND_CLEANUP(cleanup, result, -ENOMEM); + } + } + base64_histogram[base64_len++] = (char) c; + } + else + { + state = DONE; + } + break; + + default: + FAIL_AND_CLEANUP(cleanup, result, -EINVAL); + } + } + while (DONE != state); + + compressed_histogram = hdr_calloc(base64_len, sizeof(uint8_t)); + compressed_len = hdr_base64_decoded_len(base64_len); + + result = hdr_base64_decode( + base64_histogram, base64_len, compressed_histogram, compressed_len); + if (result != 0) + { + goto cleanup; + } + + result = hdr_decode_compressed(compressed_histogram, compressed_len, histogram); + +cleanup: + hdr_free(base64_histogram); + hdr_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*) hdr_calloc(encoded_len + 1, sizeof(char)); + + rc = hdr_base64_encode( + compressed_histogram, compressed_len, encoded_histogram_tmp, encoded_len); + if (rc != 0) + { + hdr_free(encoded_histogram_tmp); + FAIL_AND_CLEANUP(cleanup, result, rc); + } + + *encoded_histogram = encoded_histogram_tmp; + +cleanup: + hdr_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*) hdr_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: + hdr_free(compressed_histogram); + + return result; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/src/main/hdr-histogram/hdr_histogram_log.h b/src/main/hdr-histogram/hdr_histogram_log.h new file mode 100644 index 0000000..494ecd0 --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram_log.h @@ -0,0 +1,236 @@ +/** + * 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 + +#define HDR_LOG_TAG_MAX_BUFFER_LEN (1024) + +#include +#include +#include + +#include "hdr-histogram/hdr_time.h" +#include "hdr-histogram/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_entry +{ + hdr_timespec start_timestamp; + hdr_timespec interval; + hdr_timespec max; + char *tag; + size_t tag_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); + +/** + * 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 [Tag=XXX,],,, + * where is the binary histogram gzip compressed and base64 encoded. + * + * The tag is option and will only be written if the tag is non-NULL and the tag_len is + * greater than 0. + * + * 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 entry Prefix information for the log line, including timestamps and tag. + * @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_entry( + struct hdr_log_writer* writer, + FILE* file, + struct hdr_log_entry* entry, + 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); + +/** + * Reads an entry from the log filling in the specified histogram and log entry struct. + * 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. + * The entry struct contains a pointer to a buffer to load the tag into. If this + * is NULL or the tag_len is 0 then it won't store the tag there. The tag value will be + * NULL-terminated if there available space in the supplied buffer. If the tag is larger + * than the supplied buffer then it will be truncated. If the caller sets the last element + * in the buffer to '\0' before calling this function and it is not '\0' after the function + * returns then the value has been truncated. + * + * @param reader 'This' pointer + * @param file The stream to read the histogram from. + * @param entry Contains all of the information from the log line that is not the histogram. + * @param histogram Pointer to allocate a histogram to or merge into. + * @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_entry( + struct hdr_log_reader* reader, FILE* file, struct hdr_log_entry *entry, struct hdr_histogram** histogram); + +/** + * 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/main/hdr-histogram/hdr_histogram_log_no_op.c b/src/main/hdr-histogram/hdr_histogram_log_no_op.c new file mode 100644 index 0000000..1402379 --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram_log_no_op.c @@ -0,0 +1,171 @@ +/** + * 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 "hdr-histogram/hdr_histogram.h" +#include "hdr-histogram/hdr_histogram_log.h" +#include "hdr-histogram/hdr_tests.h" + +#define UNUSED(x) (void)(x) + +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); + } +} + +int hdr_encode_compressed( + struct hdr_histogram* h, + uint8_t** compressed_histogram, + size_t* compressed_len) +{ + UNUSED(h); + UNUSED(compressed_histogram); + UNUSED(compressed_len); + + return -1; +} + +int hdr_decode_compressed( + uint8_t* buffer, size_t length, struct hdr_histogram** histogram) +{ + UNUSED(buffer); + UNUSED(length); + UNUSED(histogram); + + return -1; +} + +int hdr_log_writer_init(struct hdr_log_writer* writer) +{ + UNUSED(writer); + + return -1; +} + +int hdr_log_write_header( + struct hdr_log_writer* writer, FILE* file, + const char* user_prefix, hdr_timespec* timestamp) +{ + UNUSED(writer); + UNUSED(file); + UNUSED(user_prefix); + UNUSED(timestamp); + + return -1; +} + +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) +{ + UNUSED(writer); + UNUSED(file); + UNUSED(start_timestamp); + UNUSED(end_timestamp); + UNUSED(histogram); + + return -1; +} + +int hdr_log_write_entry( + struct hdr_log_writer* writer, + FILE* file, + struct hdr_log_entry* entry, + struct hdr_histogram* histogram) +{ + UNUSED(writer); + UNUSED(file); + UNUSED(entry); + UNUSED(histogram); + + return -1; +} + +int hdr_log_reader_init(struct hdr_log_reader* reader) +{ + UNUSED(reader); + + return -1; +} + +int hdr_log_read_header(struct hdr_log_reader* reader, FILE* file) +{ + UNUSED(reader); + UNUSED(file); + + return -1; +} + +int hdr_log_read( + struct hdr_log_reader* reader, FILE* file, struct hdr_histogram** histogram, + hdr_timespec* timestamp, hdr_timespec* interval) +{ + UNUSED(reader); + UNUSED(file); + UNUSED(histogram); + UNUSED(timestamp); + UNUSED(interval); + + return -1; +} + +int hdr_log_read_entry( + struct hdr_log_reader* reader, FILE* file, struct hdr_log_entry *entry, struct hdr_histogram** histogram) +{ + UNUSED(reader); + UNUSED(file); + UNUSED(entry); + UNUSED(histogram); + + return -1; +} + +int hdr_log_encode(struct hdr_histogram* histogram, char** encoded_histogram) +{ + UNUSED(histogram); + UNUSED(encoded_histogram); + + return -1; +} + +int hdr_log_decode(struct hdr_histogram** histogram, char* base64_histogram, size_t base64_len) +{ + UNUSED(histogram); + UNUSED(base64_histogram); + UNUSED(base64_len); + + return -1; +} diff --git a/src/main/hdr-histogram/hdr_histogram_version.h b/src/main/hdr-histogram/hdr_histogram_version.h new file mode 100644 index 0000000..a9a4300 --- /dev/null +++ b/src/main/hdr-histogram/hdr_histogram_version.h @@ -0,0 +1,12 @@ +/** + * hdr_histogram_version.h + * Written by Marco Ippolito, Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#ifndef HDR_HISTOGRAM_VERSION_H +#define HDR_HISTOGRAM_VERSION_H + +#define HDR_HISTOGRAM_VERSION "0.11.8" + +#endif // HDR_HISTOGRAM_VERSION_H diff --git a/src/main/hdr-histogram/hdr_interval_recorder.c b/src/main/hdr-histogram/hdr_interval_recorder.c new file mode 100644 index 0000000..d7a549b --- /dev/null +++ b/src/main/hdr-histogram/hdr_interval_recorder.c @@ -0,0 +1,227 @@ +/** + * 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-histogram/hdr_interval_recorder.h" +#include "hdr-histogram/hdr_atomic.h" + +#ifndef HDR_MALLOC_INCLUDE +#define HDR_MALLOC_INCLUDE "hdr-histogram/hdr_malloc.h" +#endif + +#include HDR_MALLOC_INCLUDE + +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_discernible_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_discernible_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_discernible_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/main/hdr-histogram/hdr_interval_recorder.h b/src/main/hdr-histogram/hdr_interval_recorder.h new file mode 100644 index 0000000..cf9fc6b --- /dev/null +++ b/src/main/hdr-histogram/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-histogram/hdr_writer_reader_phaser.h" +#include "hdr-histogram/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 +); + +/** + * This is generally the preferred approach for recycling 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/main/hdr-histogram/hdr_malloc.h b/src/main/hdr-histogram/hdr_malloc.h new file mode 100644 index 0000000..6fefc68 --- /dev/null +++ b/src/main/hdr-histogram/hdr_malloc.h @@ -0,0 +1,19 @@ +/** + * hdr_malloc.h + * Written by Filipe Oliveira and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + * + * Allocator selection. + * + * This file is used in order to change the HdrHistogram allocator at compile time. + * Just define the following defines to what you want to use. Also add + * the include of your alternate allocator if needed (not needed in order + * to use the default libc allocator). */ + +#ifndef HDR_MALLOC_H__ +#define HDR_MALLOC_H__ +#define hdr_malloc malloc +#define hdr_calloc calloc +#define hdr_realloc realloc +#define hdr_free free +#endif diff --git a/src/main/hdr-histogram/hdr_tests.h b/src/main/hdr-histogram/hdr_tests.h new file mode 100644 index 0000000..21a82a1 --- /dev/null +++ b/src/main/hdr-histogram/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/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/main/hdr-histogram/hdr_thread.c b/src/main/hdr-histogram/hdr_thread.c new file mode 100644 index 0000000..262a29b --- /dev/null +++ b/src/main/hdr-histogram/hdr_thread.c @@ -0,0 +1,108 @@ +/** +* 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-histogram/hdr_thread.h" + +#ifndef HDR_MALLOC_INCLUDE +#define HDR_MALLOC_INCLUDE "hdr-histogram/hdr_malloc.h" +#endif + +#include HDR_MALLOC_INCLUDE + +struct hdr_mutex* hdr_mutex_alloc(void) +{ + return hdr_malloc(sizeof(hdr_mutex)); +} + +void hdr_mutex_free(struct hdr_mutex* mutex) +{ + hdr_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(void) +{ + sched_yield(); +} + +int hdr_usleep(unsigned int useconds) +{ + return usleep(useconds); +} + + +#endif diff --git a/src/main/hdr-histogram/hdr_thread.h b/src/main/hdr-histogram/hdr_thread.h new file mode 100644 index 0000000..549a848 --- /dev/null +++ b/src/main/hdr-histogram/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/main/hdr-histogram/hdr_time.c b/src/main/hdr-histogram/hdr_time.c new file mode 100644 index 0000000..99df9a3 --- /dev/null +++ b/src/main/hdr-histogram/hdr_time.c @@ -0,0 +1,107 @@ +/** +* 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 + +#include "hdr-histogram/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; +} + +int64_t hdr_timespec_diff_us(const hdr_timespec* t0, const hdr_timespec* t1) +{ + int64_t delta_us; + + delta_us = (t1->tv_sec - t0->tv_sec) * 1000000; + delta_us += (t1->tv_nsec - t0->tv_nsec) / 1000; + + return delta_us; +} \ No newline at end of file diff --git a/src/main/hdr-histogram/hdr_time.h b/src/main/hdr-histogram/hdr_time.h new file mode 100644 index 0000000..c4e7565 --- /dev/null +++ b/src/main/hdr-histogram/hdr_time.h @@ -0,0 +1,50 @@ +/** + * 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); + +int64_t hdr_timespec_diff_us(const hdr_timespec* t0, const hdr_timespec* t1); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/main/hdr-histogram/hdr_writer_reader_phaser.c b/src/main/hdr-histogram/hdr_writer_reader_phaser.c new file mode 100644 index 0000000..ac51dcf --- /dev/null +++ b/src/main/hdr-histogram/hdr_writer_reader_phaser.c @@ -0,0 +1,142 @@ +/** + * 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-histogram/hdr_thread.h" +#include "hdr-histogram/hdr_writer_reader_phaser.h" +#include "hdr-histogram/hdr_atomic.h" + +#ifndef HDR_MALLOC_INCLUDE +#define HDR_MALLOC_INCLUDE "hdr-histogram/hdr_malloc.h" +#endif + +#include HDR_MALLOC_INCLUDE + +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/main/hdr-histogram/hdr_writer_reader_phaser.h b/src/main/hdr-histogram/hdr_writer_reader_phaser.h new file mode 100644 index 0000000..336d810 --- /dev/null +++ b/src/main/hdr-histogram/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-histogram/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/src/main/iidxio-async/Module.mk b/src/main/iidxio-async/Module.mk new file mode 100644 index 0000000..d852e4e --- /dev/null +++ b/src/main/iidxio-async/Module.mk @@ -0,0 +1,9 @@ +dlls += iidxio-async + +ldflags_iidxio-async := \ + +libs_iidxio-async := \ + util \ + +src_iidxio-async := \ + iidxio.c \ diff --git a/src/main/iidxio-async/iidxio-async.def b/src/main/iidxio-async/iidxio-async.def new file mode 100644 index 0000000..403900d --- /dev/null +++ b/src/main/iidxio-async/iidxio-async.def @@ -0,0 +1,18 @@ +LIBRARY iidxio + +EXPORTS + iidx_io_ep1_send + iidx_io_ep1_set_deck_lights + iidx_io_ep1_set_panel_lights + iidx_io_ep1_set_top_lamps + iidx_io_ep1_set_top_neons + iidx_io_ep2_get_keys + iidx_io_ep2_get_panel + iidx_io_ep2_get_sys + iidx_io_ep2_get_slider + iidx_io_ep2_get_turntable + iidx_io_ep2_recv + iidx_io_ep3_write_16seg + iidx_io_fini + iidx_io_init + iidx_io_set_loggers diff --git a/src/main/iidxio-async/iidxio.c b/src/main/iidxio-async/iidxio.c new file mode 100644 index 0000000..24c8826 --- /dev/null +++ b/src/main/iidxio-async/iidxio.c @@ -0,0 +1,414 @@ +#define LOG_MODULE "iidxio-async" + +#include + +#include +#include +#include +#include + +#include + +#include "bemanitools/iidxio.h" + +#include "util/log.h" +#include "util/thread.h" +#include "util/time.h" + +typedef void (*iidx_io_set_loggers_t)( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal); +typedef bool (*iidx_io_init_t)( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy); +typedef void (*iidx_io_fini_t)(void); +typedef void (*iidx_io_ep1_set_deck_lights_t)(uint16_t deck_lights); +typedef void (*iidx_io_ep1_set_panel_lights_t)(uint8_t panel_lights); +typedef void (*iidx_io_ep1_set_top_lamps_t)(uint8_t top_lamps); +typedef void (*iidx_io_ep1_set_top_neons_t)(bool top_neons); +typedef bool (*iidx_io_ep1_send_t)(void); +typedef bool (*iidx_io_ep2_recv_t)(void); +typedef uint8_t (*iidx_io_ep2_get_turntable_t)(uint8_t player_no); +typedef uint8_t (*iidx_io_ep2_get_slider_t)(uint8_t slider_no); +typedef uint8_t (*iidx_io_ep2_get_sys_t)(void); +typedef uint8_t (*iidx_io_ep2_get_panel_t)(void); +typedef uint16_t (*iidx_io_ep2_get_keys_t)(void); +typedef bool (*iidx_io_ep3_write_16seg_t)(const char *text); + +static HMODULE _child_iidx_io_module; + +static iidx_io_set_loggers_t _child_iidx_io_set_loggers; +static iidx_io_init_t _child_iidx_io_init; +static iidx_io_fini_t _child_iidx_io_fini; + +static iidx_io_ep1_set_deck_lights_t _child_iidx_io_ep1_set_deck_lights; +static iidx_io_ep1_set_panel_lights_t _child_iidx_io_ep1_set_panel_lights; +static iidx_io_ep1_set_top_lamps_t _child_iidx_io_ep1_set_top_lamps; +static iidx_io_ep1_set_top_neons_t _child_iidx_io_ep1_set_top_neons; +static iidx_io_ep1_send_t _child_iidx_io_ep1_send; +static iidx_io_ep2_recv_t _child_iidx_io_ep2_recv; +static iidx_io_ep2_get_turntable_t _child_iidx_io_ep2_get_turntable; +static iidx_io_ep2_get_slider_t _child_iidx_io_ep2_get_slider; +static iidx_io_ep2_get_sys_t _child_iidx_io_ep2_get_sys; +static iidx_io_ep2_get_panel_t _child_iidx_io_ep2_get_panel; +static iidx_io_ep2_get_keys_t _child_iidx_io_ep2_get_keys; +static iidx_io_ep3_write_16seg_t _child_iidx_io_ep3_write_16seg; + +static log_formatter_t _log_formatter_misc; +static log_formatter_t _log_formatter_info; +static log_formatter_t _log_formatter_warning; +static log_formatter_t _log_formatter_fatal; + +static _Atomic(bool) _io_thread_proc_loop; +static _Atomic(bool) _io_thread_proc_running; + +static _Atomic(uint16_t) _child_iidx_io_deck_lights; +static _Atomic(uint8_t) _child_iidx_io_panel_lights; +static _Atomic(uint8_t) _child_iidx_io_top_lamps; +static _Atomic(bool) _child_iidx_io_top_neons; + +static _Atomic(uint8_t) _child_iidx_io_turntable_p1; +static _Atomic(uint8_t) _child_iidx_io_turntable_p2; +static _Atomic(uint8_t) _child_iidx_io_slider_1; +static _Atomic(uint8_t) _child_iidx_io_slider_2; +static _Atomic(uint8_t) _child_iidx_io_slider_3; +static _Atomic(uint8_t) _child_iidx_io_slider_4; +static _Atomic(uint8_t) _child_iidx_io_slider_5; +static _Atomic(uint8_t) _child_iidx_io_sys; +static _Atomic(uint8_t) _child_iidx_io_panel; +static _Atomic(uint16_t) _child_iidx_io_keys; + +static int _io_thread_proc(void *ctx) +{ + uint64_t time_start; + uint64_t time_end; + uint64_t loop_counter; + uint64_t total_time; + + bool result; + + // TODO compare previous outputs with new ones and only apply if changed? doesn't make sense for iidx as setting the values is not supposed to trigger any actual IO driving + // uint32_t prev_child_ddr_io_data_extio_lights; + // uint32_t prev_child_ddr_io_data_p3io_lights; + + // uint32_t local_tmp; + + atomic_store_explicit(&_io_thread_proc_running, true, memory_order_seq_cst); + + log_info("IO thread running"); + + time_start = time_get_counter(); + loop_counter = 0; + + // TODO have a setting to configure polling rates of inputs, outputs and 16seg separately + while (atomic_load_explicit(&_io_thread_proc_loop, memory_order_seq_cst)) { + // TODO measure time of expensive calls independently + result = _child_iidx_io_ep2_recv(); + + if (!result) { + log_warning("_child_iidx_io_ep2_recv returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + atomic_store_explicit(&_child_iidx_io_turntable_p1, _child_iidx_io_ep2_get_turntable(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_turntable_p2, _child_iidx_io_ep2_get_turntable(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_1, _child_iidx_io_ep2_get_slider(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_2, _child_iidx_io_ep2_get_slider(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_3, _child_iidx_io_ep2_get_slider(2), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_4, _child_iidx_io_ep2_get_slider(3), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_5, _child_iidx_io_ep2_get_slider(4), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_sys, _child_iidx_io_ep2_get_sys(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_panel, _child_iidx_io_ep2_get_panel(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_keys, _child_iidx_io_ep2_get_keys(), memory_order_relaxed); + + _child_iidx_io_ep1_set_deck_lights(atomic_load_explicit(&_child_iidx_io_deck_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_panel_lights(atomic_load_explicit(&_child_iidx_io_panel_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_lamps(atomic_load_explicit(&_child_iidx_io_top_lamps, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_neons(atomic_load_explicit(&_child_iidx_io_top_neons, memory_order_relaxed)); + + // TODO guard and only update every X loops? -> configurable? + result = _child_iidx_io_ep1_send(); + + if (!result) { + log_warning("_child_iidx_io_ep1_send returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + // TODO atomic write of 16seg with mutex? + // result = _child_iidx_io_ep3_write_16seg(); + + // if (!result) { + // log_warning("_child_iidx_io_ep3_write_16seg returned false"); + // atomic_store_explicit( + // &_io_thread_proc_running, false, memory_order_seq_cst); + + // log_info("IO thread shut down"); + + // return 0; + // } + + // local_tmp = _child_ddr_io_read_pad(); + + // atomic_store_explicit( + // &_child_ddr_io_data_pad, local_tmp, memory_order_relaxed); + + + // local_tmp = atomic_load_explicit( + // &_child_ddr_io_data_extio_lights, memory_order_relaxed); + + // if (local_tmp != prev_child_ddr_io_data_extio_lights) { + // _child_ddr_io_set_lights_extio(local_tmp); + // prev_child_ddr_io_data_extio_lights = local_tmp; + // } + + // local_tmp = atomic_load_explicit( + // &_child_ddr_io_data_p3io_lights, memory_order_relaxed); + + // if (local_tmp != prev_child_ddr_io_data_p3io_lights) { + // _child_ddr_io_set_lights_p3io(local_tmp); + // prev_child_ddr_io_data_p3io_lights = local_tmp; + // } + + // Don't hog the CPU + SwitchToThread(); + + loop_counter++; + } + + time_end = time_get_counter(); + total_time = time_get_elapsed_us(time_end - time_start); + + log_info( + "IO thread performance: total iterations %lld, avg. loop cycle time %f " + "us", + loop_counter, + ((double) total_time) / loop_counter); + + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; +} + +static void *_load_function(HMODULE module, const char *name) +{ + void *ptr; + + ptr = GetProcAddress(module, name); + + if (ptr == NULL) { + log_fatal("Could not find function %s in iidxio child library", name); + } + + return ptr; +} + +void iidx_io_set_loggers( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal) +{ + _log_formatter_misc = misc; + _log_formatter_info = info; + _log_formatter_warning = warning; + _log_formatter_fatal = fatal; + + log_to_external(misc, info, warning, fatal); +} + +bool iidx_io_init( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy) +{ + log_info("Loading iidxio-async-child.dll as child iidxio library..."); + + _child_iidx_io_module = LoadLibraryA("iidxio-async-child.dll"); + + if (_child_iidx_io_module == NULL) { + log_warning("Loading iidxio-async-child.dll failed"); + return false; + } + + _child_iidx_io_set_loggers = + _load_function(_child_iidx_io_module, "iidx_io_set_loggers"); + _child_iidx_io_init = _load_function(_child_iidx_io_module, "iidx_io_init"); + _child_iidx_io_fini = _load_function(_child_iidx_io_module, "iidx_io_fini"); + + _child_iidx_io_ep1_set_deck_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_deck_lights"); + _child_iidx_io_ep1_set_panel_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_panel_lights"); + _child_iidx_io_ep1_set_top_lamps = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_lamps"); + _child_iidx_io_ep1_set_top_neons = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_neons"); + _child_iidx_io_ep1_send = + _load_function(_child_iidx_io_module, "iidx_io_ep1_send"); + _child_iidx_io_ep2_recv = + _load_function(_child_iidx_io_module, "iidx_io_ep2_recv"); + _child_iidx_io_ep2_get_turntable = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_turntable"); + _child_iidx_io_ep2_get_slider = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_slider"); + _child_iidx_io_ep2_get_sys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_sys"); + _child_iidx_io_ep2_get_panel = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_panel"); + _child_iidx_io_ep2_get_keys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_keys"); + _child_iidx_io_ep3_write_16seg = + _load_function(_child_iidx_io_module, "iidx_io_ep3_write_16seg"); + + _child_iidx_io_set_loggers( + _log_formatter_misc, + _log_formatter_info, + _log_formatter_warning, + _log_formatter_fatal); + + log_info("Calling child iidx_io_init..."); + + if (!_child_iidx_io_init(thread_create, thread_join, thread_destroy)) { + log_warning("Child iidx_io_init failed"); + FreeLibrary(_child_iidx_io_module); + + return false; + } + + atomic_store_explicit(&_io_thread_proc_loop, true, memory_order_seq_cst); + + if (!thread_create(_io_thread_proc, NULL, 16384, 0)) { + log_warning("Creating IO thread failed"); + + _child_iidx_io_fini(); + FreeLibrary(_child_iidx_io_module); + + return false; + } + + return true; +} + +void iidx_io_fini(void) +{ + atomic_store_explicit(&_io_thread_proc_loop, false, memory_order_seq_cst); + + log_info("Shutting down IO thread and waiting for it to finish..."); + + while ( + atomic_load_explicit(&_io_thread_proc_running, memory_order_seq_cst)) { + Sleep(1); + } + + log_info("IO thread finished"); + + _child_iidx_io_fini(); + + FreeLibrary(_child_iidx_io_module); +} + +void iidx_io_ep1_set_deck_lights(uint16_t deck_lights) +{ + atomic_store_explicit( + &_child_iidx_io_deck_lights, deck_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_panel_lights(uint8_t panel_lights) +{ + atomic_store_explicit( + &_child_iidx_io_panel_lights, panel_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_lamps(uint8_t top_lamps) +{ + atomic_store_explicit( + &_child_iidx_io_top_lamps, top_lamps, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_neons(bool top_neons) +{ + atomic_store_explicit( + &_child_iidx_io_top_neons, top_neons, memory_order_relaxed); +} + +bool iidx_io_ep1_send(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +bool iidx_io_ep2_recv(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +uint8_t iidx_io_ep2_get_turntable(uint8_t player_no) +{ + switch (player_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_turntable_p1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_turntable_p2, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_slider(uint8_t slider_no) +{ + switch (slider_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_slider_1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_slider_2, memory_order_relaxed); + case 2: + return atomic_load_explicit(&_child_iidx_io_slider_3, memory_order_relaxed); + case 3: + return atomic_load_explicit(&_child_iidx_io_slider_4, memory_order_relaxed); + case 4: + return atomic_load_explicit(&_child_iidx_io_slider_5, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_sys(void) +{ + return atomic_load_explicit(&_child_iidx_io_sys, memory_order_relaxed); +} + +uint8_t iidx_io_ep2_get_panel(void) +{ + return atomic_load_explicit(&_child_iidx_io_panel, memory_order_relaxed); +} + +uint16_t iidx_io_ep2_get_keys(void) +{ + return atomic_load_explicit(&_child_iidx_io_keys, memory_order_relaxed); +} + +bool iidx_io_ep3_write_16seg(const char *text) +{ + // TODO atomic copy with mutex? + + return true; +} \ No newline at end of file diff --git a/src/main/iidxio-perf/Module.mk b/src/main/iidxio-perf/Module.mk new file mode 100644 index 0000000..7df509f --- /dev/null +++ b/src/main/iidxio-perf/Module.mk @@ -0,0 +1,10 @@ +dlls += iidxio-perf + +ldflags_iidxio-perf := \ + +libs_iidxio-perf := \ + hdr-histogram \ + util \ + +src_iidxio-perf := \ + iidxio.c \ diff --git a/src/main/iidxio-perf/iidxio-perf.def b/src/main/iidxio-perf/iidxio-perf.def new file mode 100644 index 0000000..403900d --- /dev/null +++ b/src/main/iidxio-perf/iidxio-perf.def @@ -0,0 +1,18 @@ +LIBRARY iidxio + +EXPORTS + iidx_io_ep1_send + iidx_io_ep1_set_deck_lights + iidx_io_ep1_set_panel_lights + iidx_io_ep1_set_top_lamps + iidx_io_ep1_set_top_neons + iidx_io_ep2_get_keys + iidx_io_ep2_get_panel + iidx_io_ep2_get_sys + iidx_io_ep2_get_slider + iidx_io_ep2_get_turntable + iidx_io_ep2_recv + iidx_io_ep3_write_16seg + iidx_io_fini + iidx_io_init + iidx_io_set_loggers diff --git a/src/main/iidxio-perf/iidxio.c b/src/main/iidxio-perf/iidxio.c new file mode 100644 index 0000000..1842a3f --- /dev/null +++ b/src/main/iidxio-perf/iidxio.c @@ -0,0 +1,306 @@ +#define LOG_MODULE "iidxio-perf" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "bemanitools/iidxio.h" + +#include "hdr-histogram/hdr_histogram.h" +#include "hdr-histogram/hdr_time.h" + +#include "util/log.h" +#include "util/thread.h" +#include "util/time.h" + +typedef void (*iidx_io_set_loggers_t)( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal); +typedef bool (*iidx_io_init_t)( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy); +typedef void (*iidx_io_fini_t)(void); +typedef void (*iidx_io_ep1_set_deck_lights_t)(uint16_t deck_lights); +typedef void (*iidx_io_ep1_set_panel_lights_t)(uint8_t panel_lights); +typedef void (*iidx_io_ep1_set_top_lamps_t)(uint8_t top_lamps); +typedef void (*iidx_io_ep1_set_top_neons_t)(bool top_neons); +typedef bool (*iidx_io_ep1_send_t)(void); +typedef bool (*iidx_io_ep2_recv_t)(void); +typedef uint8_t (*iidx_io_ep2_get_turntable_t)(uint8_t player_no); +typedef uint8_t (*iidx_io_ep2_get_slider_t)(uint8_t slider_no); +typedef uint8_t (*iidx_io_ep2_get_sys_t)(void); +typedef uint8_t (*iidx_io_ep2_get_panel_t)(void); +typedef uint16_t (*iidx_io_ep2_get_keys_t)(void); +typedef bool (*iidx_io_ep3_write_16seg_t)(const char *text); + +static HMODULE _child_iidx_io_module; + +static iidx_io_set_loggers_t _child_iidx_io_set_loggers; +static iidx_io_init_t _child_iidx_io_init; +static iidx_io_fini_t _child_iidx_io_fini; + +static iidx_io_ep1_set_deck_lights_t _child_iidx_io_ep1_set_deck_lights; +static iidx_io_ep1_set_panel_lights_t _child_iidx_io_ep1_set_panel_lights; +static iidx_io_ep1_set_top_lamps_t _child_iidx_io_ep1_set_top_lamps; +static iidx_io_ep1_set_top_neons_t _child_iidx_io_ep1_set_top_neons; +static iidx_io_ep1_send_t _child_iidx_io_ep1_send; +static iidx_io_ep2_recv_t _child_iidx_io_ep2_recv; +static iidx_io_ep2_get_turntable_t _child_iidx_io_ep2_get_turntable; +static iidx_io_ep2_get_slider_t _child_iidx_io_ep2_get_slider; +static iidx_io_ep2_get_sys_t _child_iidx_io_ep2_get_sys; +static iidx_io_ep2_get_panel_t _child_iidx_io_ep2_get_panel; +static iidx_io_ep2_get_keys_t _child_iidx_io_ep2_get_keys; +static iidx_io_ep3_write_16seg_t _child_iidx_io_ep3_write_16seg; + +static log_formatter_t _log_formatter_misc; +static log_formatter_t _log_formatter_info; +static log_formatter_t _log_formatter_warning; +static log_formatter_t _log_formatter_fatal; + +struct hdr_histogram* _histogram_ep1_send; +struct hdr_histogram* _histogram_ep2_recv; +struct hdr_histogram* _histogram_ep3_write_16seg; + +static void *_load_function(HMODULE module, const char *name) +{ + void *ptr; + + ptr = GetProcAddress(module, name); + + if (ptr == NULL) { + log_fatal("Could not find function %s in iidxio child library", name); + } + + return ptr; +} + +void iidx_io_set_loggers( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal) +{ + _log_formatter_misc = misc; + _log_formatter_info = info; + _log_formatter_warning = warning; + _log_formatter_fatal = fatal; + + log_to_external(misc, info, warning, fatal); +} + +bool iidx_io_init( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy) +{ + size_t histogram_memory_usage_bytes; + + log_info("Loading iidxio-perf-child.dll as child iidxio library..."); + + _child_iidx_io_module = LoadLibraryA("iidxio-perf-child.dll"); + + if (_child_iidx_io_module == NULL) { + log_warning("Loading iidxio-perf-child.dll failed"); + return false; + } + + _child_iidx_io_set_loggers = + _load_function(_child_iidx_io_module, "iidx_io_set_loggers"); + _child_iidx_io_init = _load_function(_child_iidx_io_module, "iidx_io_init"); + _child_iidx_io_fini = _load_function(_child_iidx_io_module, "iidx_io_fini"); + + _child_iidx_io_ep1_set_deck_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_deck_lights"); + _child_iidx_io_ep1_set_panel_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_panel_lights"); + _child_iidx_io_ep1_set_top_lamps = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_lamps"); + _child_iidx_io_ep1_set_top_neons = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_neons"); + _child_iidx_io_ep1_send = + _load_function(_child_iidx_io_module, "iidx_io_ep1_send"); + _child_iidx_io_ep2_recv = + _load_function(_child_iidx_io_module, "iidx_io_ep2_recv"); + _child_iidx_io_ep2_get_turntable = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_turntable"); + _child_iidx_io_ep2_get_slider = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_slider"); + _child_iidx_io_ep2_get_sys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_sys"); + _child_iidx_io_ep2_get_panel = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_panel"); + _child_iidx_io_ep2_get_keys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_keys"); + _child_iidx_io_ep3_write_16seg = + _load_function(_child_iidx_io_module, "iidx_io_ep3_write_16seg"); + + _child_iidx_io_set_loggers( + _log_formatter_misc, + _log_formatter_info, + _log_formatter_warning, + _log_formatter_fatal); + + log_info("Initialising histograms..."); + + hdr_init(1, INT64_C(3600000000), 3, &_histogram_ep1_send); + hdr_init(1, INT64_C(3600000000), 3, &_histogram_ep2_recv); + hdr_init(1, INT64_C(3600000000), 3, &_histogram_ep3_write_16seg); + + histogram_memory_usage_bytes = + hdr_get_memory_size(_histogram_ep1_send) + + hdr_get_memory_size(_histogram_ep2_recv) + + hdr_get_memory_size(_histogram_ep3_write_16seg); + + log_misc("Histogram total memory usage: %" PRIdPTR " bytes", histogram_memory_usage_bytes); + + log_info("Calling child iidx_io_init..."); + + if (!_child_iidx_io_init(thread_create, thread_join, thread_destroy)) { + log_warning("Child iidx_io_init failed"); + FreeLibrary(_child_iidx_io_module); + + return false; + } else { + return true; + } +} + +void iidx_io_fini(void) +{ + _child_iidx_io_fini(); + + log_info("----------------------------------------------------------------"); + log_info("Printing histograms..."); + log_info("All values are times in microseconds (us)"); + + log_info("----------------------------------------------------------------"); + log_info(">>> EP1 send"); + hdr_percentiles_print(_histogram_ep1_send, stdout, 5, 1.0, CLASSIC); + log_info("----------------------------------------------------------------"); + log_info(">>> EP2 recv"); + hdr_percentiles_print(_histogram_ep2_recv, stdout, 5, 1.0, CLASSIC); + log_info("----------------------------------------------------------------"); + log_info(">>> EP3 write 16seg"); + hdr_percentiles_print(_histogram_ep3_write_16seg, stdout, 5, 1.0, CLASSIC); + + log_info("----------------------------------------------------------------"); + + fflush(stdout); + + hdr_close(_histogram_ep1_send); + hdr_close(_histogram_ep2_recv); + hdr_close(_histogram_ep3_write_16seg); + + FreeLibrary(_child_iidx_io_module); +} + +void iidx_io_ep1_set_deck_lights(uint16_t deck_lights) +{ + return _child_iidx_io_ep1_set_deck_lights(deck_lights); +} + +void iidx_io_ep1_set_panel_lights(uint8_t panel_lights) +{ + return _child_iidx_io_ep1_set_panel_lights(panel_lights); +} + +void iidx_io_ep1_set_top_lamps(uint8_t top_lamps) +{ + return _child_iidx_io_ep1_set_top_lamps(top_lamps); +} + +void iidx_io_ep1_set_top_neons(bool top_neons) +{ + return _child_iidx_io_ep1_set_top_neons(top_neons); +} + +bool iidx_io_ep1_send(void) +{ + hdr_timespec start_time; + hdr_timespec end_time; + int64_t diff_us; + bool result; + + hdr_gettime(&start_time); + + result = _child_iidx_io_ep1_send(); + + hdr_gettime(&end_time); + diff_us = hdr_timespec_diff_us(&start_time, &end_time); + + hdr_record_value(_histogram_ep1_send, diff_us); + + return result; +} + +bool iidx_io_ep2_recv(void) +{ + hdr_timespec start_time; + hdr_timespec end_time; + int64_t diff_us; + bool result; + + hdr_gettime(&start_time); + + result = _child_iidx_io_ep2_recv(); + + hdr_gettime(&end_time); + diff_us = hdr_timespec_diff_us(&start_time, &end_time); + + hdr_record_value(_histogram_ep2_recv, diff_us); + + return result; +} + +uint8_t iidx_io_ep2_get_turntable(uint8_t player_no) +{ + return _child_iidx_io_ep2_get_turntable(player_no); +} + +uint8_t iidx_io_ep2_get_slider(uint8_t slider_no) +{ + return _child_iidx_io_ep2_get_slider(slider_no); +} + +uint8_t iidx_io_ep2_get_sys(void) +{ + return _child_iidx_io_ep2_get_sys(); +} + +uint8_t iidx_io_ep2_get_panel(void) +{ + return _child_iidx_io_ep2_get_panel(); +} + +uint16_t iidx_io_ep2_get_keys(void) +{ + return _child_iidx_io_ep2_get_keys(); +} + +bool iidx_io_ep3_write_16seg(const char *text) +{ + hdr_timespec start_time; + hdr_timespec end_time; + int64_t diff_us; + bool result; + + hdr_gettime(&start_time); + + result = _child_iidx_io_ep3_write_16seg(text); + + hdr_gettime(&end_time); + diff_us = hdr_timespec_diff_us(&start_time, &end_time); + + hdr_record_value(_histogram_ep3_write_16seg, diff_us); + + return result; +}