mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 03:45:09 -05:00
Compare commits
3 Commits
pr293
...
frame-paci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed34be6ec3 | ||
|
|
8ec9ec712c | ||
|
|
3d868e33d4 |
@@ -135,6 +135,7 @@ include src/main/ezusb2-popn-shim/Module.mk
|
|||||||
include src/main/ezusb2-tool/Module.mk
|
include src/main/ezusb2-tool/Module.mk
|
||||||
include src/main/ezusb2/Module.mk
|
include src/main/ezusb2/Module.mk
|
||||||
include src/main/geninput/Module.mk
|
include src/main/geninput/Module.mk
|
||||||
|
include src/main/hdr-histogram/Module.mk
|
||||||
include src/main/hook/Module.mk
|
include src/main/hook/Module.mk
|
||||||
include src/main/hooklib/Module.mk
|
include src/main/hooklib/Module.mk
|
||||||
include src/main/iidx-bio2-exit-hook/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/iidxhook7/Module.mk
|
||||||
include src/main/iidxhook8/Module.mk
|
include src/main/iidxhook8/Module.mk
|
||||||
include src/main/iidxhook9/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-bio2/Module.mk
|
||||||
include src/main/iidxio-ezusb/Module.mk
|
include src/main/iidxio-ezusb/Module.mk
|
||||||
include src/main/iidxio-ezusb2/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/iidxio/Module.mk
|
||||||
include src/main/iidxiotest/Module.mk
|
include src/main/iidxiotest/Module.mk
|
||||||
include src/main/inject/Module.mk
|
include src/main/inject/Module.mk
|
||||||
@@ -456,9 +459,11 @@ $(zipdir)/iidx-27-to-30.zip: \
|
|||||||
$(zipdir)/iidx-hwio-x86.zip: \
|
$(zipdir)/iidx-hwio-x86.zip: \
|
||||||
build/bin/indep-32/aciomgr.dll \
|
build/bin/indep-32/aciomgr.dll \
|
||||||
build/bin/indep-32/eamio-icca.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-bio2.dll \
|
||||||
build/bin/indep-32/iidxio-ezusb.dll \
|
build/bin/indep-32/iidxio-ezusb.dll \
|
||||||
build/bin/indep-32/iidxio-ezusb2.dll \
|
build/bin/indep-32/iidxio-ezusb2.dll \
|
||||||
|
build/bin/indep-32/iidxio-perf.dll \
|
||||||
dist/iidx/iidxio-bio2.conf \
|
dist/iidx/iidxio-bio2.conf \
|
||||||
build/bin/indep-32/vigem-iidxio.exe \
|
build/bin/indep-32/vigem-iidxio.exe \
|
||||||
dist/iidx/vigem-iidxio.conf \
|
dist/iidx/vigem-iidxio.conf \
|
||||||
@@ -469,9 +474,11 @@ $(zipdir)/iidx-hwio-x86.zip: \
|
|||||||
$(zipdir)/iidx-hwio-x64.zip: \
|
$(zipdir)/iidx-hwio-x64.zip: \
|
||||||
build/bin/indep-64/aciomgr.dll \
|
build/bin/indep-64/aciomgr.dll \
|
||||||
build/bin/indep-64/eamio-icca.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-bio2.dll \
|
||||||
build/bin/indep-64/iidxio-ezusb.dll \
|
build/bin/indep-64/iidxio-ezusb.dll \
|
||||||
build/bin/indep-64/iidxio-ezusb2.dll \
|
build/bin/indep-64/iidxio-ezusb2.dll \
|
||||||
|
build/bin/indep-64/iidxio-perf.dll \
|
||||||
dist/iidx/iidxio-bio2.conf \
|
dist/iidx/iidxio-bio2.conf \
|
||||||
build/bin/indep-64/vigem-iidxio.exe \
|
build/bin/indep-64/vigem-iidxio.exe \
|
||||||
dist/iidx/vigem-iidxio.conf \
|
dist/iidx/vigem-iidxio.conf \
|
||||||
|
|||||||
3
src/main/debug-overlay/dllmain.c
Normal file
3
src/main/debug-overlay/dllmain.c
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// TODO have a separate dll that acts as a debug
|
||||||
|
// overlay for btools in general providing stuff
|
||||||
|
// like frame graph, text sections for IO timings etc.
|
||||||
10
src/main/hdr-histogram/Module.mk
Normal file
10
src/main/hdr-histogram/Module.mk
Normal file
@@ -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 \
|
||||||
146
src/main/hdr-histogram/hdr_atomic.h
Normal file
146
src/main/hdr-histogram/hdr_atomic.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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__ */
|
||||||
322
src/main/hdr-histogram/hdr_encoding.c
Normal file
322
src/main/hdr-histogram/hdr_encoding.c
Normal file
@@ -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 <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
79
src/main/hdr-histogram/hdr_encoding.h
Normal file
79
src/main/hdr-histogram/hdr_encoding.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
116
src/main/hdr-histogram/hdr_endian.h
Normal file
116
src/main/hdr-histogram/hdr_endian.h
Normal file
@@ -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 <endian.h>
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
# include <libkern/OSByteOrder.h>
|
||||||
|
|
||||||
|
# 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 <sys/endian.h>
|
||||||
|
|
||||||
|
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||||
|
|
||||||
|
# include <sys/endian.h>
|
||||||
|
|
||||||
|
# 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 <winsock2.h>
|
||||||
|
|
||||||
|
# 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
|
||||||
1226
src/main/hdr-histogram/hdr_histogram.c
Normal file
1226
src/main/hdr-histogram/hdr_histogram.c
Normal file
File diff suppressed because it is too large
Load Diff
516
src/main/hdr-histogram/hdr_histogram.h
Normal file
516
src/main/hdr-histogram/hdr_histogram.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
|
||||||
|
*/
|
||||||
|
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
|
||||||
1290
src/main/hdr-histogram/hdr_histogram_log.c
Normal file
1290
src/main/hdr-histogram/hdr_histogram_log.c
Normal file
File diff suppressed because it is too large
Load Diff
236
src/main/hdr-histogram/hdr_histogram_log.h
Normal file
236
src/main/hdr-histogram/hdr_histogram_log.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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 <start timestamp>,<end timestamp>,<max>,<histogram>
|
||||||
|
* where <histogram> 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,]<start timestamp>,<end timestamp>,<max>,<histogram>
|
||||||
|
* where <histogram> 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
|
||||||
171
src/main/hdr-histogram/hdr_histogram_log_no_op.c
Normal file
171
src/main/hdr-histogram/hdr_histogram_log_no_op.c
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
12
src/main/hdr-histogram/hdr_histogram_version.h
Normal file
12
src/main/hdr-histogram/hdr_histogram_version.h
Normal file
@@ -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
|
||||||
227
src/main/hdr-histogram/hdr_interval_recorder.c
Normal file
227
src/main/hdr-histogram/hdr_interval_recorder.c
Normal file
@@ -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];
|
||||||
|
}
|
||||||
109
src/main/hdr-histogram/hdr_interval_recorder.h
Normal file
109
src/main/hdr-histogram/hdr_interval_recorder.h
Normal file
@@ -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
|
||||||
19
src/main/hdr-histogram/hdr_malloc.h
Normal file
19
src/main/hdr-histogram/hdr_malloc.h
Normal file
@@ -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
|
||||||
22
src/main/hdr-histogram/hdr_tests.h
Normal file
22
src/main/hdr-histogram/hdr_tests.h
Normal file
@@ -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
|
||||||
108
src/main/hdr-histogram/hdr_thread.c
Normal file
108
src/main/hdr-histogram/hdr_thread.c
Normal file
@@ -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 <stdlib.h>
|
||||||
|
#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 <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
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 <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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
|
||||||
55
src/main/hdr-histogram/hdr_thread.h
Normal file
55
src/main/hdr-histogram/hdr_thread.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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 <pthread.h>
|
||||||
|
|
||||||
|
#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
|
||||||
107
src/main/hdr-histogram/hdr_time.c
Normal file
107
src/main/hdr-histogram/hdr_time.c
Normal file
@@ -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 <math.h>
|
||||||
|
|
||||||
|
#include "hdr-histogram/hdr_time.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
|
||||||
|
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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 <mach/clock.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
50
src/main/hdr-histogram/hdr_time.h
Normal file
50
src/main/hdr-histogram/hdr_time.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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
|
||||||
142
src/main/hdr-histogram/hdr_writer_reader_phaser.c
Normal file
142
src/main/hdr-histogram/hdr_writer_reader_phaser.c
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
51
src/main/hdr-histogram/hdr_writer_reader_phaser.h
Normal file
51
src/main/hdr-histogram/hdr_writer_reader_phaser.h
Normal file
@@ -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 <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -6,3 +6,4 @@ libs_iidxhook-d3d9 := \
|
|||||||
|
|
||||||
src_iidxhook-d3d9 := \
|
src_iidxhook-d3d9 := \
|
||||||
bb-scale-hd.c \
|
bb-scale-hd.c \
|
||||||
|
frame-pace.c \
|
||||||
|
|||||||
194
src/main/iidxhook-d3d9/frame-pace.c
Normal file
194
src/main/iidxhook-d3d9/frame-pace.c
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
#define LOG_MODULE "iidxhook-d3d-frame-pace"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "hook/table.h"
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "frame-pace.h"
|
||||||
|
|
||||||
|
static void STDCALL my_Sleep(DWORD dwMilliseconds);
|
||||||
|
static void (STDCALL *real_Sleep)(DWORD dwMilliseconds);
|
||||||
|
|
||||||
|
static DWORD STDCALL my_SleepEx(DWORD dwMilliseconds, BOOL bAlertable);
|
||||||
|
static DWORD (STDCALL *real_SleepEx)(DWORD dwMilliseconds, BOOL bAlertable);
|
||||||
|
|
||||||
|
static const struct hook_symbol iidxhok_d3d9_frame_pace_hook_syms[] = {
|
||||||
|
{
|
||||||
|
.name = "Sleep",
|
||||||
|
.patch = my_Sleep,
|
||||||
|
.link = (void **) &real_Sleep,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "SleepEx",
|
||||||
|
.patch = my_SleepEx,
|
||||||
|
.link = (void **) &real_SleepEx,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool iidxhook_d3d9_frame_pace_initialized;
|
||||||
|
|
||||||
|
static DWORD iidxhook_d3d9_frame_pace_main_thread_id = -1;
|
||||||
|
|
||||||
|
static int64_t iidxhook_d3d9_frame_pace_target_frame_time_cpu_ticks;
|
||||||
|
static int64_t iidxhook_d3d9_frame_pace_frame_time_start_cpu_ticks;
|
||||||
|
|
||||||
|
static uint64_t iidxhook_d3d9_frame_pace_get_cpu_tick_frequency()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
|
||||||
|
return freq.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t iidxhook_d3d9_frame_pace_get_cpu_ticks()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER tick;
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&tick);
|
||||||
|
|
||||||
|
return tick.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source and reference implementation:
|
||||||
|
// https://github.com/PCSX2/pcsx2/blob/f26031cada6893ac306af73255d337e50a8f73f9/pcsx2/Counters.cpp#L563
|
||||||
|
static void iidxhook_d3d9_frame_pace_do_post_frame()
|
||||||
|
{
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
const int64_t m_iTicks = iidxhook_d3d9_frame_pace_target_frame_time_cpu_ticks;
|
||||||
|
int64_t m_iStart = iidxhook_d3d9_frame_pace_frame_time_start_cpu_ticks;
|
||||||
|
|
||||||
|
// Compute when we would expect this frame to end, assuming everything goes perfectly perfect.
|
||||||
|
const uint64_t uExpectedEnd = m_iStart + m_iTicks;
|
||||||
|
// The current tick we actually stopped on.
|
||||||
|
const uint64_t iEnd = iidxhook_d3d9_frame_pace_get_cpu_ticks();
|
||||||
|
// The diff between when we stopped and when we expected to.
|
||||||
|
const int64_t sDeltaTime = iEnd - uExpectedEnd;
|
||||||
|
|
||||||
|
// If frame ran too long...
|
||||||
|
if (sDeltaTime >= m_iTicks)
|
||||||
|
{
|
||||||
|
// ... Fudge the next frame start over a bit. Prevents fast forward zoomies.
|
||||||
|
m_iStart += (sDeltaTime / m_iTicks) * m_iTicks;
|
||||||
|
iidxhook_d3d9_frame_pace_frame_time_start_cpu_ticks = m_iStart;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion of delta from CPU ticks (microseconds) to milliseconds
|
||||||
|
int32_t msec = (int32_t) ((sDeltaTime * -1000) / (int64_t) iidxhook_d3d9_frame_pace_get_cpu_tick_frequency());
|
||||||
|
|
||||||
|
// If any integer value of milliseconds exists, sleep it off.
|
||||||
|
// Prior comments suggested that 1-2 ms sleeps were inaccurate on some OSes;
|
||||||
|
// further testing suggests instead that this was utter bullshit.
|
||||||
|
if (msec > 1)
|
||||||
|
{
|
||||||
|
real_Sleep(msec - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion to milliseconds loses some precision; after sleeping off whole milliseconds,
|
||||||
|
// spin the thread without sleeping until we finally reach our expected end time.
|
||||||
|
while (iidxhook_d3d9_frame_pace_get_cpu_ticks() < uExpectedEnd)
|
||||||
|
{
|
||||||
|
// SKREEEEEEEE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, set our next frame start to when this one ends
|
||||||
|
m_iStart = uExpectedEnd;
|
||||||
|
iidxhook_d3d9_frame_pace_frame_time_start_cpu_ticks = m_iStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO must be renamed to framerate monitor with smoother/pacer
|
||||||
|
// TODO have feature flag to print framerate performance counters etc every X seconds
|
||||||
|
// as misc debug log output
|
||||||
|
// TODO make sure to record a decent amount of data/frame time accordingly over these
|
||||||
|
// seconds to report proper avg. frame time/rate, min, max, p95, p99, p999
|
||||||
|
// TODO move this to a separate module that can be re-used on d3d9ex
|
||||||
|
|
||||||
|
// fill up unused frametime on short frames to simulate hardware accuracy
|
||||||
|
// and match the timing of the target monitor's refresh rate as close as possible
|
||||||
|
// this fixes frame pacing issues with too short frames not being smoothened
|
||||||
|
// correctly by the game which either relies entirely on the hardware/GPU driver
|
||||||
|
// to do that or on tricoro+ era games, on SleepEx which only has max of 1 ms
|
||||||
|
// accuracy. the further the target monitor refresh rate is away from the desired
|
||||||
|
// refresh rate, e.g. 60 hz vsync, the more apparent the frame pacing issues
|
||||||
|
// become in the form of "random stuttering during gameplay"
|
||||||
|
|
||||||
|
static void STDCALL my_Sleep(DWORD dwMilliseconds)
|
||||||
|
{
|
||||||
|
// Heuristic, but seems to kill the poorly implemented frame pacing code
|
||||||
|
// fairly reliable without impacting other parts of the code negatively
|
||||||
|
if (iidxhook_d3d9_frame_pace_main_thread_id == GetCurrentThreadId()) {
|
||||||
|
if (dwMilliseconds <= 16) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
real_Sleep(dwMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD STDCALL my_SleepEx(DWORD dwMilliseconds, BOOL bAlertable)
|
||||||
|
{
|
||||||
|
// Heuristic, but applies only in two spots
|
||||||
|
// - frame pacing code (dynamic value)
|
||||||
|
// - Another spot with sleep time set to 1 -> reduces CPU banging
|
||||||
|
if (iidxhook_d3d9_frame_pace_main_thread_id == GetCurrentThreadId()) {
|
||||||
|
if (dwMilliseconds <= 16) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_SleepEx(dwMilliseconds, bAlertable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iidxhook_d3d9_frame_pace_timings_init(double target_frame_rate_hz)
|
||||||
|
{
|
||||||
|
double tick_rate;
|
||||||
|
|
||||||
|
tick_rate = iidxhook_d3d9_frame_pace_get_cpu_tick_frequency();
|
||||||
|
iidxhook_d3d9_frame_pace_target_frame_time_cpu_ticks = (int64_t) (tick_rate / target_frame_rate_hz);
|
||||||
|
iidxhook_d3d9_frame_pace_frame_time_start_cpu_ticks = iidxhook_d3d9_frame_pace_get_cpu_ticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidxhook_d3d9_frame_pace_init(DWORD main_thread_id, double target_frame_rate_hz)
|
||||||
|
{
|
||||||
|
log_assert(main_thread_id != -1);
|
||||||
|
|
||||||
|
iidxhook_d3d9_frame_pace_timings_init(target_frame_rate_hz);
|
||||||
|
|
||||||
|
iidxhook_d3d9_frame_pace_main_thread_id = main_thread_id;
|
||||||
|
|
||||||
|
iidxhook_d3d9_frame_pace_initialized = true;
|
||||||
|
|
||||||
|
hook_table_apply(
|
||||||
|
NULL, "kernel32.dll", iidxhok_d3d9_frame_pace_hook_syms, lengthof(iidxhok_d3d9_frame_pace_hook_syms));
|
||||||
|
|
||||||
|
log_info("Initialized, target frame rate in hz %f, target frame time in cpu ticks %llu", target_frame_rate_hz, iidxhook_d3d9_frame_pace_target_frame_time_cpu_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT iidxhook_d3d9_frame_pace_d3d9_irp_handler(struct hook_d3d9_irp *irp)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
log_assert(irp);
|
||||||
|
|
||||||
|
if (!iidxhook_d3d9_frame_pace_initialized) {
|
||||||
|
return hook_d3d9_irp_invoke_next(irp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irp->op == HOOK_D3D9_IRP_OP_DEV_PRESENT) {
|
||||||
|
hr = hook_d3d9_irp_invoke_next(irp);
|
||||||
|
|
||||||
|
if (hr == S_OK) {
|
||||||
|
iidxhook_d3d9_frame_pace_do_post_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
} else {
|
||||||
|
return hook_d3d9_irp_invoke_next(irp);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main/iidxhook-d3d9/frame-pace.h
Normal file
13
src/main/iidxhook-d3d9/frame-pace.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef IIDXHOOK_D3D9_FRAME_PACE_H
|
||||||
|
#define IIDXHOOK_D3D9_FRAME_PCE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "hook/d3d9.h"
|
||||||
|
|
||||||
|
void iidxhook_d3d9_frame_pace_init(DWORD main_thread_id, double target_frame_rate_hz);
|
||||||
|
|
||||||
|
HRESULT iidxhook_d3d9_frame_pace_d3d9_irp_handler(struct hook_d3d9_irp *irp);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -16,5 +16,6 @@ src_iidxhook-util := \
|
|||||||
d3d9.c \
|
d3d9.c \
|
||||||
eamuse.c \
|
eamuse.c \
|
||||||
effector.c \
|
effector.c \
|
||||||
|
frame-graph.c \
|
||||||
log-server.c \
|
log-server.c \
|
||||||
settings.c \
|
settings.c \
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
|
||||||
#include "iidxhook-util/d3d9.h"
|
#include "iidxhook-util/d3d9.h"
|
||||||
|
#include "iidxhook-util/frame-graph.h"
|
||||||
#include "iidxhook-util/vertex-shader.h"
|
#include "iidxhook-util/vertex-shader.h"
|
||||||
|
|
||||||
#include "util/defs.h"
|
#include "util/defs.h"
|
||||||
@@ -232,7 +233,7 @@ iidxhook_util_d3d9_log_create_device_params(struct hook_d3d9_irp *irp)
|
|||||||
"hDeviceWindow %p, Windowed %d, "
|
"hDeviceWindow %p, Windowed %d, "
|
||||||
"EnableAutoDepthStencil "
|
"EnableAutoDepthStencil "
|
||||||
"%d, AutoDepthStencilFormat %d, Flags %lX, "
|
"%d, AutoDepthStencilFormat %d, Flags %lX, "
|
||||||
"FullScreen_RefreshRateInHz %d",
|
"FullScreen_RefreshRateInHz %d, PresentationInterval %d",
|
||||||
irp->args.ctx_create_device.pp->BackBufferWidth,
|
irp->args.ctx_create_device.pp->BackBufferWidth,
|
||||||
irp->args.ctx_create_device.pp->BackBufferHeight,
|
irp->args.ctx_create_device.pp->BackBufferHeight,
|
||||||
irp->args.ctx_create_device.pp->BackBufferFormat,
|
irp->args.ctx_create_device.pp->BackBufferFormat,
|
||||||
@@ -244,7 +245,8 @@ iidxhook_util_d3d9_log_create_device_params(struct hook_d3d9_irp *irp)
|
|||||||
irp->args.ctx_create_device.pp->EnableAutoDepthStencil,
|
irp->args.ctx_create_device.pp->EnableAutoDepthStencil,
|
||||||
irp->args.ctx_create_device.pp->AutoDepthStencilFormat,
|
irp->args.ctx_create_device.pp->AutoDepthStencilFormat,
|
||||||
irp->args.ctx_create_device.pp->Flags,
|
irp->args.ctx_create_device.pp->Flags,
|
||||||
irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz);
|
irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz,
|
||||||
|
irp->args.ctx_create_device.pp->PresentationInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -267,6 +269,9 @@ static void iidxhook_util_d3d9_fix_create_device_apply_window_mode(
|
|||||||
log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE);
|
log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE);
|
||||||
D3DPRESENT_PARAMETERS *pp = irp->args.ctx_create_device.pp;
|
D3DPRESENT_PARAMETERS *pp = irp->args.ctx_create_device.pp;
|
||||||
|
|
||||||
|
// TODO temporarily remove vsync for testing
|
||||||
|
pp->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||||
|
|
||||||
if (iidxhook_util_d3d9_config.windowed) {
|
if (iidxhook_util_d3d9_config.windowed) {
|
||||||
pp->Windowed = TRUE;
|
pp->Windowed = TRUE;
|
||||||
pp->FullScreen_RefreshRateInHz = 0;
|
pp->FullScreen_RefreshRateInHz = 0;
|
||||||
@@ -1190,7 +1195,14 @@ iidxhook_util_d3d9_irp_handler(struct hook_d3d9_irp *irp)
|
|||||||
|
|
||||||
return hook_d3d9_irp_invoke_next(irp);
|
return hook_d3d9_irp_invoke_next(irp);
|
||||||
|
|
||||||
|
// TODO is there always ever just a single scene being rendered?
|
||||||
|
case HOOK_D3D9_IRP_OP_DEV_END_SCENE:
|
||||||
|
DrawFrameGraph(irp->args.dev_present.self);
|
||||||
|
|
||||||
|
return hook_d3d9_irp_invoke_next(irp);
|
||||||
|
|
||||||
case HOOK_D3D9_IRP_OP_DEV_PRESENT:
|
case HOOK_D3D9_IRP_OP_DEV_PRESENT:
|
||||||
|
|
||||||
iidxhook_util_d3d9_scale_render_target_to_back_buffer(irp);
|
iidxhook_util_d3d9_scale_render_target_to_back_buffer(irp);
|
||||||
iidxhook_util_d3d9_set_back_buffer_rt(irp);
|
iidxhook_util_d3d9_set_back_buffer_rt(irp);
|
||||||
|
|
||||||
|
|||||||
374
src/main/iidxhook-util/frame-graph.c
Normal file
374
src/main/iidxhook-util/frame-graph.c
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <d3d9.h>
|
||||||
|
#include <d3dx9.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/time.h"
|
||||||
|
|
||||||
|
#define MAX_SAMPLES 600 // 10 seconds at 60 FPS
|
||||||
|
#define GRAPH_WIDTH 300
|
||||||
|
#define GRAPH_HEIGHT 100
|
||||||
|
#define GRAPH_X 300 // Left position of graph
|
||||||
|
#define GRAPH_Y 30 // Top position of graph
|
||||||
|
#define AXIS_COLOR D3DCOLOR_XRGB(128, 128, 128) // Gray color for axes
|
||||||
|
#define GRAPH_COLOR D3DCOLOR_XRGB(255, 255, 0) // Yellow color for the graph
|
||||||
|
#define TEXT_COLOR D3DCOLOR_XRGB(255, 255, 0) // Yellow color for text
|
||||||
|
|
||||||
|
// // TODO have a separate module that wraps all of these somewhat
|
||||||
|
typedef HRESULT WINAPI (*func_D3DXCreateFontA)(
|
||||||
|
struct IDirect3DDevice9 *device,
|
||||||
|
INT height,
|
||||||
|
UINT width,
|
||||||
|
UINT weight,
|
||||||
|
UINT miplevels,
|
||||||
|
BOOL italic,
|
||||||
|
DWORD charset,
|
||||||
|
DWORD precision,
|
||||||
|
DWORD quality,
|
||||||
|
DWORD pitchandfamily,
|
||||||
|
const char *facename,
|
||||||
|
struct ID3DXFont **font);
|
||||||
|
|
||||||
|
typedef HRESULT WINAPI (*func_D3DXCreateLine)(
|
||||||
|
struct IDirect3DDevice9 *device,
|
||||||
|
struct ID3DXLine **line);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double frameTimes[MAX_SAMPLES];
|
||||||
|
int currentIndex;
|
||||||
|
double minTime;
|
||||||
|
double maxTime;
|
||||||
|
double avgTime;
|
||||||
|
int sampleCount;
|
||||||
|
|
||||||
|
ID3DXFont* font; // For regular text
|
||||||
|
ID3DXFont* smallFont; // For axis labels
|
||||||
|
ID3DXLine* line;
|
||||||
|
bool resourcesInitialized;
|
||||||
|
} PerfMetrics;
|
||||||
|
|
||||||
|
PerfMetrics g_metrics = {0};
|
||||||
|
|
||||||
|
static HRESULT createLine(
|
||||||
|
IDirect3DDevice9 *dev,
|
||||||
|
struct ID3DXLine **line)
|
||||||
|
{
|
||||||
|
HMODULE d3d9_24;
|
||||||
|
|
||||||
|
d3d9_24 = GetModuleHandleA("d3dx9_24.dll");
|
||||||
|
|
||||||
|
if (d3d9_24 == NULL) {
|
||||||
|
log_fatal(
|
||||||
|
"Failed to load d3dx9_24.dll to create a font for displaying "
|
||||||
|
"framerate on monitor check.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func_D3DXCreateLine d3dxCreateLine =
|
||||||
|
(func_D3DXCreateLine) GetProcAddress(d3d9_24, "D3DXCreateLine");
|
||||||
|
|
||||||
|
if (d3dxCreateLine == NULL) {
|
||||||
|
log_fatal("Failed to find function D3DXCreateLine");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d3dxCreateLine(
|
||||||
|
dev,
|
||||||
|
line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT createFontA(
|
||||||
|
IDirect3DDevice9 *dev,
|
||||||
|
INT height,
|
||||||
|
UINT width,
|
||||||
|
UINT weight,
|
||||||
|
UINT miplevels,
|
||||||
|
BOOL italic,
|
||||||
|
DWORD charset,
|
||||||
|
DWORD precision,
|
||||||
|
DWORD quality,
|
||||||
|
DWORD pitchandfamily,
|
||||||
|
const char *facename,
|
||||||
|
ID3DXFont **font)
|
||||||
|
{
|
||||||
|
HMODULE d3d9_24;
|
||||||
|
|
||||||
|
d3d9_24 = GetModuleHandleA("d3dx9_24.dll");
|
||||||
|
|
||||||
|
if (d3d9_24 == NULL) {
|
||||||
|
log_fatal(
|
||||||
|
"Failed to load d3dx9_24.dll to create a font for displaying "
|
||||||
|
"framerate on monitor check.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func_D3DXCreateFontA d3dxCreateFontA =
|
||||||
|
(func_D3DXCreateFontA) GetProcAddress(d3d9_24, "D3DXCreateFontA");
|
||||||
|
|
||||||
|
if (d3dxCreateFontA == NULL) {
|
||||||
|
log_fatal("Failed to find function D3DXCreateFontA");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d3dxCreateFontA(
|
||||||
|
dev,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
weight,
|
||||||
|
miplevels,
|
||||||
|
italic,
|
||||||
|
charset,
|
||||||
|
precision,
|
||||||
|
quality,
|
||||||
|
pitchandfamily,
|
||||||
|
facename,
|
||||||
|
font);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update performance metrics with new frame time
|
||||||
|
void UpdateMetrics(double frameTime) {
|
||||||
|
g_metrics.frameTimes[g_metrics.currentIndex] = frameTime;
|
||||||
|
g_metrics.currentIndex = (g_metrics.currentIndex + 1) % MAX_SAMPLES;
|
||||||
|
|
||||||
|
if (g_metrics.sampleCount < MAX_SAMPLES)
|
||||||
|
g_metrics.sampleCount++;
|
||||||
|
|
||||||
|
// Update min/max/avg
|
||||||
|
g_metrics.minTime = frameTime;
|
||||||
|
g_metrics.maxTime = frameTime;
|
||||||
|
g_metrics.avgTime = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < g_metrics.sampleCount; i++) {
|
||||||
|
double time = g_metrics.frameTimes[i];
|
||||||
|
g_metrics.minTime = min(g_metrics.minTime, time);
|
||||||
|
g_metrics.maxTime = max(g_metrics.maxTime, time);
|
||||||
|
g_metrics.avgTime += time;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_metrics.avgTime /= g_metrics.sampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InitializeOverlayResources(IDirect3DDevice9* device) {
|
||||||
|
if (g_metrics.resourcesInitialized) return true;
|
||||||
|
|
||||||
|
// Create main font (larger, for FPS display)
|
||||||
|
if (FAILED(createFontA(device, 20, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
|
||||||
|
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
|
||||||
|
"Arial", &g_metrics.font))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create smaller font for axis labels
|
||||||
|
if (FAILED(createFontA(device, 12, 0, FW_NORMAL, 1, FALSE, DEFAULT_CHARSET,
|
||||||
|
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
|
||||||
|
"Arial", &g_metrics.smallFont))) {
|
||||||
|
ID3DXFont_Release(g_metrics.font);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(createLine(device, &g_metrics.line))) {
|
||||||
|
ID3DXFont_Release(g_metrics.font);
|
||||||
|
ID3DXFont_Release(g_metrics.smallFont);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_metrics.resourcesInitialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawGraph(IDirect3DDevice9* device) {
|
||||||
|
// Draw the frame time graph
|
||||||
|
D3DXVECTOR2 points[MAX_SAMPLES];
|
||||||
|
float xStep = (float)GRAPH_WIDTH / (MAX_SAMPLES - 1);
|
||||||
|
|
||||||
|
// Find min/max times in current sample range
|
||||||
|
double minTime = g_metrics.frameTimes[0];
|
||||||
|
double maxTime = g_metrics.frameTimes[0];
|
||||||
|
for (int i = 0; i < g_metrics.sampleCount; i++) {
|
||||||
|
int idx = (g_metrics.currentIndex - g_metrics.sampleCount + i + MAX_SAMPLES) % MAX_SAMPLES;
|
||||||
|
double time = g_metrics.frameTimes[idx];
|
||||||
|
minTime = min(minTime, time);
|
||||||
|
maxTime = max(maxTime, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add small padding to prevent graph from touching edges
|
||||||
|
double padding = (maxTime - minTime) * 0.1;
|
||||||
|
maxTime += padding;
|
||||||
|
minTime = max(0, minTime - padding);
|
||||||
|
|
||||||
|
float yScale = (float)GRAPH_HEIGHT / (maxTime - minTime);
|
||||||
|
|
||||||
|
for (int i = 0; i < g_metrics.sampleCount; i++) {
|
||||||
|
int idx = (g_metrics.currentIndex - g_metrics.sampleCount + i + MAX_SAMPLES) % MAX_SAMPLES;
|
||||||
|
points[i].x = GRAPH_X + i * xStep;
|
||||||
|
points[i].y = GRAPH_Y + GRAPH_HEIGHT - (g_metrics.frameTimes[idx] - minTime) * yScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3DXLine_SetWidth(g_metrics.line, 1.0f);
|
||||||
|
ID3DXLine_Draw(g_metrics.line, points, g_metrics.sampleCount, GRAPH_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add option to have fixed min and max for the Y scale (so that the graph doesn't scale when the frame rate changes)
|
||||||
|
// keep this an option though with uncapped to also see really bad frame times on screen
|
||||||
|
void DrawOverlay(IDirect3DDevice9* device) {
|
||||||
|
if (!InitializeOverlayResources(device))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw black background
|
||||||
|
D3DRECT rect = {GRAPH_X, GRAPH_Y - 30, GRAPH_X + GRAPH_WIDTH, GRAPH_Y + GRAPH_HEIGHT};
|
||||||
|
IDirect3DDevice9_Clear(device, 1, &rect, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0);
|
||||||
|
|
||||||
|
// Draw X and Y axes
|
||||||
|
D3DXVECTOR2 xAxis[] = {
|
||||||
|
{ .x = GRAPH_X, .y = GRAPH_Y + GRAPH_HEIGHT },
|
||||||
|
{ .x = GRAPH_X + GRAPH_WIDTH, .y = GRAPH_Y + GRAPH_HEIGHT }
|
||||||
|
};
|
||||||
|
|
||||||
|
D3DXVECTOR2 yAxis[] = {
|
||||||
|
{ .x = GRAPH_X, .y = GRAPH_Y },
|
||||||
|
{ .x = GRAPH_X, .y = GRAPH_Y + GRAPH_HEIGHT }
|
||||||
|
};
|
||||||
|
|
||||||
|
ID3DXLine_SetWidth(g_metrics.line, 1.0f);
|
||||||
|
ID3DXLine_Draw(g_metrics.line, xAxis, 2, AXIS_COLOR);
|
||||||
|
ID3DXLine_Draw(g_metrics.line, yAxis, 2, AXIS_COLOR);
|
||||||
|
|
||||||
|
// Draw X axis labels (time)
|
||||||
|
char buffer[32];
|
||||||
|
RECT textRect;
|
||||||
|
for (int i = 0; i <= 10; i++) {
|
||||||
|
float x = GRAPH_X + (GRAPH_WIDTH * i / 10.0f);
|
||||||
|
sprintf(buffer, "%.1fs", (10.0f - i));
|
||||||
|
textRect.left = (LONG)x - 15;
|
||||||
|
textRect.top = GRAPH_Y + GRAPH_HEIGHT + 5;
|
||||||
|
textRect.right = (LONG)x + 15;
|
||||||
|
textRect.bottom = GRAPH_Y + GRAPH_HEIGHT + 20;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.smallFont, NULL, buffer, -1, &textRect, DT_CENTER, AXIS_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find min/max times in current sample range
|
||||||
|
double minTime = g_metrics.frameTimes[0];
|
||||||
|
double maxTime = g_metrics.frameTimes[0];
|
||||||
|
for (int i = 0; i < g_metrics.sampleCount; i++) {
|
||||||
|
int idx = (g_metrics.currentIndex - g_metrics.sampleCount + i + MAX_SAMPLES) % MAX_SAMPLES;
|
||||||
|
double time = g_metrics.frameTimes[idx];
|
||||||
|
minTime = min(minTime, time);
|
||||||
|
maxTime = max(maxTime, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
float yScale = (float)GRAPH_HEIGHT / (maxTime - minTime);
|
||||||
|
|
||||||
|
// Draw Y axis labels (frame time)
|
||||||
|
for (int i = 0; i <= 8; i++) {
|
||||||
|
float y = GRAPH_Y + GRAPH_HEIGHT - (GRAPH_HEIGHT * i / 8.0f);
|
||||||
|
float ms = minTime + ((maxTime - minTime) * i / 8.0f);
|
||||||
|
sprintf(buffer, "%.1fms", ms);
|
||||||
|
textRect.left = GRAPH_X - 45;
|
||||||
|
textRect.top = (LONG)y - 6;
|
||||||
|
textRect.right = GRAPH_X - 5;
|
||||||
|
textRect.bottom = (LONG)y + 6;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.smallFont, NULL, buffer, -1, &textRect, DT_RIGHT, AXIS_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add some kind of coloring to the frame rate graph/line whenever the line is above or below a certain threshold of the reference line
|
||||||
|
|
||||||
|
// Draw 60 FPS reference line (16.67ms)
|
||||||
|
const float targetFrameTime = 1000.0f/60.0f; // 16.67ms
|
||||||
|
float y60fps = GRAPH_Y + GRAPH_HEIGHT - ((targetFrameTime - minTime) * yScale);
|
||||||
|
|
||||||
|
// Only draw reference line if it's within the visible range
|
||||||
|
if (targetFrameTime >= minTime && targetFrameTime <= maxTime) {
|
||||||
|
D3DXVECTOR2 refLine[2];
|
||||||
|
refLine[0].x = GRAPH_X;
|
||||||
|
refLine[0].y = y60fps;
|
||||||
|
refLine[1].x = GRAPH_X + GRAPH_WIDTH;
|
||||||
|
refLine[1].y = y60fps;
|
||||||
|
|
||||||
|
ID3DXLine_SetWidth(g_metrics.line, 1.0f);
|
||||||
|
ID3DXLine_Draw(g_metrics.line, refLine, 2, D3DCOLOR_ARGB(128, 255, 255, 0)); // Semi-transparent yellow
|
||||||
|
|
||||||
|
// Draw reference line label
|
||||||
|
char refBuffer[32];
|
||||||
|
sprintf(refBuffer, "16.67ms (60 FPS)");
|
||||||
|
RECT refTextRect;
|
||||||
|
refTextRect.left = GRAPH_X + GRAPH_WIDTH + 5;
|
||||||
|
refTextRect.top = y60fps - 8; // Center text vertically with line
|
||||||
|
refTextRect.right = refTextRect.left + 100;
|
||||||
|
refTextRect.bottom = refTextRect.top + 16;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.smallFont, NULL, refBuffer, -1, &refTextRect, DT_LEFT, D3DCOLOR_ARGB(128, 255, 255, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw the frame time graph
|
||||||
|
D3DXVECTOR2 points[MAX_SAMPLES];
|
||||||
|
float xStep = (float)GRAPH_WIDTH / (MAX_SAMPLES - 1);
|
||||||
|
|
||||||
|
// Add small padding to prevent graph from touching edges
|
||||||
|
double padding = (maxTime - minTime) * 0.1;
|
||||||
|
maxTime += padding;
|
||||||
|
minTime = max(0, minTime - padding);
|
||||||
|
|
||||||
|
for (int i = 0; i < g_metrics.sampleCount; i++) {
|
||||||
|
int idx = (g_metrics.currentIndex - g_metrics.sampleCount + i + MAX_SAMPLES) % MAX_SAMPLES;
|
||||||
|
points[i].x = GRAPH_X + i * xStep;
|
||||||
|
points[i].y = GRAPH_Y + GRAPH_HEIGHT - (g_metrics.frameTimes[idx] - minTime) * yScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3DXLine_SetWidth(g_metrics.line, 1.0f);
|
||||||
|
ID3DXLine_Draw(g_metrics.line, points, g_metrics.sampleCount, GRAPH_COLOR);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Calculate current FPS
|
||||||
|
double currentFrameTime = g_metrics.frameTimes[(g_metrics.currentIndex - 1 + MAX_SAMPLES) % MAX_SAMPLES];
|
||||||
|
double fps = 1000.0 / currentFrameTime;
|
||||||
|
|
||||||
|
// Draw FPS text
|
||||||
|
sprintf(buffer, "%.3f FPS", fps);
|
||||||
|
textRect.left = GRAPH_X + GRAPH_WIDTH - 100;
|
||||||
|
textRect.top = GRAPH_Y - 25;
|
||||||
|
textRect.right = GRAPH_X + GRAPH_WIDTH;
|
||||||
|
textRect.bottom = GRAPH_Y - 5;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.font, NULL, buffer, -1, &textRect, DT_RIGHT, TEXT_COLOR);
|
||||||
|
|
||||||
|
// Draw current frame time in ms
|
||||||
|
sprintf(buffer, "%.3f ms", currentFrameTime);
|
||||||
|
textRect.left = GRAPH_X + GRAPH_WIDTH - 200; // Position to the left of FPS
|
||||||
|
textRect.top = GRAPH_Y - 25;
|
||||||
|
textRect.right = GRAPH_X + GRAPH_WIDTH - 110; // Leave space for FPS text
|
||||||
|
textRect.bottom = GRAPH_Y - 5;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.font, NULL, buffer, -1, &textRect, DT_RIGHT, TEXT_COLOR);
|
||||||
|
|
||||||
|
// TODO draw derivation graph and average derivation time over the current frame scope
|
||||||
|
|
||||||
|
// Draw "Framerate" label
|
||||||
|
textRect.left = GRAPH_X;
|
||||||
|
textRect.top = GRAPH_Y - 25;
|
||||||
|
textRect.right = GRAPH_X + 100;
|
||||||
|
textRect.bottom = GRAPH_Y - 5;
|
||||||
|
ID3DXFont_DrawTextA(g_metrics.font, NULL, "Framerate", -1, &textRect, DT_LEFT, TEXT_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooked Present function
|
||||||
|
void DrawFrameGraph(IDirect3DDevice9* device) {
|
||||||
|
|
||||||
|
static uint64_t lastTime = 0;
|
||||||
|
uint64_t currentTime = time_get_counter();
|
||||||
|
|
||||||
|
if (lastTime != 0) {
|
||||||
|
uint64_t frameTime = currentTime - lastTime;
|
||||||
|
double frameTimeMs = time_get_elapsed_us(frameTime) / 1000.0;
|
||||||
|
UpdateMetrics(frameTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
//IDirect3DDevice9_BeginScene(device);
|
||||||
|
DrawOverlay(device);
|
||||||
|
//IDirect3DDevice9_EndScene(device);
|
||||||
|
}
|
||||||
10
src/main/iidxhook-util/frame-graph.h
Normal file
10
src/main/iidxhook-util/frame-graph.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef IIDXHOOK_FRAME_GRAPH_H
|
||||||
|
#define IIDXHOOK_FRAME_GRAPH_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <d3d9.h>
|
||||||
|
#include <d3dx9.h>
|
||||||
|
|
||||||
|
void DrawFrameGraph(IDirect3DDevice9* device);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -116,6 +116,8 @@ static int log_thread_proc(void *ctx)
|
|||||||
static void
|
static void
|
||||||
log_post(char level, const char *module, const char *fmt, va_list ap)
|
log_post(char level, const char *module, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
|
// TODO test if this addresses performance issues and stuttering
|
||||||
|
// TODO measure time how long waiting takes here?
|
||||||
if (WaitForSingleObject(log_rv_producer, INFINITE)) {
|
if (WaitForSingleObject(log_rv_producer, INFINITE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,8 @@ skip:
|
|||||||
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
|
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
|
||||||
{
|
{
|
||||||
if (reason == DLL_PROCESS_ATTACH) {
|
if (reason == DLL_PROCESS_ATTACH) {
|
||||||
log_to_writer(log_writer_debug, NULL);
|
// TODO switched to null writer to see if it addresses performance issues
|
||||||
|
log_to_writer(log_writer_null, NULL);
|
||||||
|
|
||||||
/* Bootstrap hook for further init tasks (see above) */
|
/* Bootstrap hook for further init tasks (see above) */
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,8 @@ skip:
|
|||||||
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
|
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
|
||||||
{
|
{
|
||||||
if (reason == DLL_PROCESS_ATTACH) {
|
if (reason == DLL_PROCESS_ATTACH) {
|
||||||
log_to_writer(log_writer_debug, NULL);
|
// TODO switched to null writer to see if it addresses performance issues
|
||||||
|
log_to_writer(log_writer_null, NULL);
|
||||||
|
|
||||||
/* Bootstrap hook for further init tasks (see above) */
|
/* Bootstrap hook for further init tasks (see above) */
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ ldflags_iidxhook3 := \
|
|||||||
|
|
||||||
libs_iidxhook3 := \
|
libs_iidxhook3 := \
|
||||||
iidxhook-util \
|
iidxhook-util \
|
||||||
|
iidxhook-d3d9 \
|
||||||
ezusb-emu \
|
ezusb-emu \
|
||||||
ezusb-iidx-16seg-emu \
|
ezusb-iidx-16seg-emu \
|
||||||
ezusb2-emu \
|
ezusb2-emu \
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
#include "hooklib/rs232.h"
|
#include "hooklib/rs232.h"
|
||||||
#include "hooklib/setupapi.h"
|
#include "hooklib/setupapi.h"
|
||||||
|
|
||||||
|
#include "iidxhook-d3d9/frame-pace.h"
|
||||||
|
|
||||||
#include "iidxhook-util/acio.h"
|
#include "iidxhook-util/acio.h"
|
||||||
#include "iidxhook-util/chart-patch.h"
|
#include "iidxhook-util/chart-patch.h"
|
||||||
#include "iidxhook-util/clock.h"
|
#include "iidxhook-util/clock.h"
|
||||||
@@ -52,6 +54,9 @@
|
|||||||
|
|
||||||
static const hook_d3d9_irp_handler_t iidxhook_d3d9_handlers[] = {
|
static const hook_d3d9_irp_handler_t iidxhook_d3d9_handlers[] = {
|
||||||
iidxhook_util_d3d9_irp_handler,
|
iidxhook_util_d3d9_irp_handler,
|
||||||
|
// Order is important for performance, frame pacing must come at the very end
|
||||||
|
// to include all timings from any prior hooks
|
||||||
|
//iidxhook_d3d9_frame_pace_d3d9_irp_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
static HANDLE STDCALL my_OpenProcess(DWORD, BOOL, DWORD);
|
static HANDLE STDCALL my_OpenProcess(DWORD, BOOL, DWORD);
|
||||||
@@ -69,6 +74,8 @@ iidxhook3_setup_d3d9_hooks(const struct iidxhook_config_gfx *config_gfx)
|
|||||||
{
|
{
|
||||||
struct iidxhook_util_d3d9_config d3d9_config;
|
struct iidxhook_util_d3d9_config d3d9_config;
|
||||||
|
|
||||||
|
iidxhook_d3d9_frame_pace_init(GetCurrentThreadId(), 60.0);
|
||||||
|
|
||||||
iidxhook_util_d3d9_init_config(&d3d9_config);
|
iidxhook_util_d3d9_init_config(&d3d9_config);
|
||||||
|
|
||||||
d3d9_config.windowed = config_gfx->windowed;
|
d3d9_config.windowed = config_gfx->windowed;
|
||||||
|
|||||||
9
src/main/iidxio-async/Module.mk
Normal file
9
src/main/iidxio-async/Module.mk
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
dlls += iidxio-async
|
||||||
|
|
||||||
|
ldflags_iidxio-async := \
|
||||||
|
|
||||||
|
libs_iidxio-async := \
|
||||||
|
util \
|
||||||
|
|
||||||
|
src_iidxio-async := \
|
||||||
|
iidxio.c \
|
||||||
18
src/main/iidxio-async/iidxio-async.def
Normal file
18
src/main/iidxio-async/iidxio-async.def
Normal file
@@ -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
|
||||||
414
src/main/iidxio-async/iidxio.c
Normal file
414
src/main/iidxio-async/iidxio.c
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
#define LOG_MODULE "iidxio-async"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
11
src/main/iidxio-perf/Module.mk
Normal file
11
src/main/iidxio-perf/Module.mk
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
dlls += iidxio-perf
|
||||||
|
|
||||||
|
ldflags_iidxio-perf := \
|
||||||
|
-lws2_32
|
||||||
|
|
||||||
|
libs_iidxio-perf := \
|
||||||
|
hdr-histogram \
|
||||||
|
util \
|
||||||
|
|
||||||
|
src_iidxio-perf := \
|
||||||
|
iidxio.c \
|
||||||
18
src/main/iidxio-perf/iidxio-perf.def
Normal file
18
src/main/iidxio-perf/iidxio-perf.def
Normal file
@@ -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
|
||||||
306
src/main/iidxio-perf/iidxio.c
Normal file
306
src/main/iidxio-perf/iidxio.c
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
#define LOG_MODULE "iidxio-perf"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user